文章目录
- 顺序表算法题
- 移除元素
- 删除有序数组中的重复项
- 合并两个有序数组
- 链表算法题
- 移除链表元素
- 反转链表
- 链表的中间结点
- 合并两个有序链表
- 链表分割
- 链表的回文结构
顺序表算法题
不熟悉顺序表的可以先了解一下
顺序表实现方法
移除元素
给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素。元素的顺序可能发生改变。然后返回nums
中与val
不同的元素的数量。假设
nums
中不等于val
的元素数量为k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。- 返回
k
。
很容易想到的是用双指针进行遍历和赋值即可
int removeElement(int* nums, int numsSize, int val) {int left = 0;for (int right = 0; right < numsSize; right++) {if (nums[right] != val) {nums[left] = nums[right];left++;}}return left;
}
-
注意题目中说的是元素的顺序可以发生改变,即不需要保持元素的相对顺序,可以进行优化
-
如果左指针 left 指向的元素等于 val,此时将右指针 right 指向的元素复制到左指针 left 的位置,然后右指针 right 左移一位。如果赋值过来的元素恰好也等于 val,可以继续把右指针 right 指向的元素的值赋值过来(左指针 left 指向的等于 val 的元素的位置继续被覆盖),直到左指针指向的元素的值不等于 val 为止。
注意
while
结束条件不要少了等于
-
int removeElement(int* nums, int numsSize, int val) {int left = 0, right = numsSize-1;while (left <= right) {if (nums[left] == val) {nums[left] = nums[right];right--;} else {left++;}}return left;
}
注意:返回的是元素的个数
删除有序数组中的重复项
给你一个 非严格递增排列 的数组
nums
,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回nums
中唯一元素的个数。考虑
nums
的唯一元素的数量为k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。- 返回
k
- 和上一道题思路大致相同,题目说了是非严格递增序列,双指针法比较前后两个元素即可
int removeDuplicates(int* nums, int numsSize) {int src = 0;int dest = 1;while (dest < numsSize) {if(nums[src]!=nums[dest]){nums[++src]=nums[dest];}dest++;}return ++src;
}
合并两个有序数组
给你两个按 非递减顺序 排列的整数数组
nums1
和nums2
,另有两个整数m
和n
,分别表示nums1
和nums2
中的元素数目。请你 合并
nums2
到nums1
中,使合并后的数组同样按 非递减顺序 排列。**注意:**最终,合并后数组不应由函数返回,而是存储在数组
nums1
中。为了应对这种情况,nums1
的初始长度为m + n
,其中前m
个元素表示应合并的元素,后n
个元素为0
,应忽略。nums2
的长度为n
。
- 当然可以直接把第二个数组移到第一个数组尾部,然后进行排序
- 使用qsort函数
int cmp(int* a, int* b) {return *a - *b;
}void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {for (int i = 0; i != n; ++i) {nums1[m + i] = nums2[i];}qsort(nums1, nums1Size, sizeof(int), cmp);
}
- 三指针法,利用两个数组都是非递减排列
- 因为排序好的数组仍然是非递减序列,所以我们的两个指针依次从尾部开始向前遍历,谁大把谁放到nums1的尾部(若从前面开始需要新创建一个数组来存储元素最后再赋值给nums1)
- 最后出循环的时候l2和l3只可能有一个小于0
- 若是l2,说明nums2没有遍历完,需要将剩下的元素赋值给nums1
- 若是l3,则直接返回nums1即可
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {int l1=nums1Size-1;int l2=m-1;int l3=n-1;while(l2>=0&&l3>=0){if(nums1[l2]>nums2[l3]){nums1[l1]=nums1[l2];l2--;}else{nums1[l1]=nums2[l3];l3--;}l1--;}if(l2<0){while(l1>=0)nums1[l1--]=nums2[l3--];}
}
链表算法题
不熟悉链表的可以先了解一下
单链表实现方法
移除链表元素
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。
- 最直观的办法就是创建一个新链表
注:不是开辟空间的深拷贝,而只是定义了指向同一结点的指针
在最后需要先判断newtail是否为空**(否则链表为空链表时会报错)再将其中的next指针置为空(否则可能会出现循环)**
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/
typedef struct ListNode ListNode;
ListNode* removeElements(ListNode* head, int val) {ListNode* pcur = head;ListNode* newhead = NULL;ListNode* newtail = NULL;while (pcur) {if (pcur->val != val) {if (newhead == NULL) {newhead = newtail = pcur;} else {newtail->next = pcur;newtail = newtail->next;}}pcur = pcur->next;}if (newtail)newtail->next = NULL;return newhead;
}
- 在leetcode上给出了递归的方法,链表的定义具有递归的性质,因此链表题目常可以用递归的方法求解。这道题要求删除链表中所有节点值等于特定值的节点,可以用递归实现。
struct ListNode* removeElements(struct ListNode* head, int val) {if (head == NULL) {return head;}head->next = removeElements(head->next, val);return head->val == val ? head->next : head;
}
反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。
- 可以和上一道题一样创建新链表,不多赘述
- 这里介绍一种比较巧妙的方法->三指针法
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {if(head==NULL)return head;ListNode* n1,*n2,*n3;n1=NULL,n2=head,n3=head->next;while(n3){n2->next=n1;n1=n2;n2=n3;n3=n3->next;}n2->next=n1;return n2;}
链表的中间结点
给你单链表的头结点
head
,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
- 快慢指针法
- 注意为偶数时返回第二个结点
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {ListNode* slow, *fast;slow=fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;}return slow;
}
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
- 和合并数组大致思路相同
- 可以在创建新链表的一开始申请头结点(哨兵位),避免对于newtail和newhead为空的情况进行讨论
- 记得最后释放空间!!!
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {ListNode* newhead,*newtail;newhead=newtail=(ListNode*)malloc(sizeof(ListNode));while(list1&&list2){if(list1->val<list2->val){newtail->next=list1;list1=list1->next;}else{newtail->next=list2;list2=list2->next;}newtail=newtail->next;}if(list1){newtail->next=list1;}else{newtail->next=list2;}ListNode* ret=newhead->next;free(newhead);newhead=newtail=NUll;return ret;
}
链表分割
有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针
- 解题思路:创建两个链表,分别存放小于x的节点和大于等于x的节点,分别进行尾插
- 最后别忘了把大链表的尾节点置为空,否则可能会出现死循环
- 还是别忘了释放空间
ListNode* partition(ListNode* pHead, int x) {if(pHead == NULL)return NULL;struct ListNode* lessHead, *lessTail,*greaterHead, *greaterTail;//创建链表表头lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* cur = pHead;while(cur){//小于x的尾插到lessTailif(cur->val < x){lessTail->next = cur;lessTail = lessTail->next;}//大于等于x的尾插到greaterTailelse{greaterTail->next = cur;greaterTail = greaterTail->next;}cur = cur->next;}//链接两个链表lessTail->next = greaterHead->next;greaterTail->next = NULL;//获取表头pHead = lessHead->next;free(lessHead);free(greaterHead); return pHead;}
链表的回文结构
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900
- 不推荐的解法,类似数组字符串的回文解法
- 先把链表中的元素值全部保存到数组中,然后再判断数组是否为回文。不建议使用这种解法,因为如果没有告诉链表最大长度,则不能同此解法
bool chkPalindrome(ListNode* A) {// write code hereint a[900] = {0};ListNode* cur = A;int n = 0;//保存链表元素while(cur){a[n++] = cur->val;cur = cur->next;}//判断数组是否为回文结构int begin = 0, end = n-1;while(begin < end){if(a[begin] != a[end])return false;++begin;--end;}return true;}
- 解题思路:此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
- 快慢指针找中间结点
- 三指针逆置后半部分
bool chkPalindrome(ListNode* A) {if (A == NULL || A->next == NULL)return true;ListNode* slow, *fast, *prev, *cur, *nxt;slow = fast = A;//找到中间节点while (fast && fast->next){slow = slow->next;fast = fast->next->next;}prev = NULL;//后半部分逆置cur = slow;while (cur){nxt = cur->next;cur->next = prev;prev = cur;cur = nxt;}//逐点比对while (A && prev){if (A->val != prev->val)return false;A = A->next;prev = prev->next;}return true;}