1. 移除链表元素
题目链接 : 移除链表元素
第一种思路 : 使用两个指针,一前一后判断
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
// 直接先判断head是否为空,head若为空就直接返回
if (head == NULL) {
return head;
}
struct ListNode* cur = head;
struct ListNode* prev = NULL;
while (cur) {
if (cur->val != val) {
prev = cur;
cur = cur->next;
} else {
// prev == NULL 代表现在是头删 ( 如果不是头删就意味着prev不会为空)
if (prev == NULL) {
head = head->next;
free(cur);
cur = head;
} else {
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
}
return head;
}
第二种思路 : 把不是val的值尾插到新链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
// newnode 是新链表的节点 tail负责找尾
struct ListNode *newnode = NULL, *tail = NULL;
struct ListNode* cur = head;
while (cur) {
if (cur->val != val) {
// 尾插
if (tail == NULL) {
// 把 cur 赋值给tail在赋值给newnode ( 因为tail为空代表现在是空链表 直接插入 )
newnode = tail = cur;
} else {
tail->next = cur;
tail = tail->next;
}
cur = cur->next;
} else {
struct ListNode* next = cur->next;
free(cur);
cur = next;
}
}
// 如果链表传入本身就为空 或者 删除某值后的链表为空,就不能让 tail->next 为空。因此在这里做判断一下
if (tail) {
tail->next = NULL;
}
return newnode;
}
2. 反转链表
题目链接 : 反转链表
思路 :
利用三个指针 ( prev、cur、next_node ),
prev
指向前一个节点;cur
指向当前节点;next_node
指向下一个节点。判断cur
是否为空,如果不为空,将下一个节点保存下来,然后将当cur->next
点指向prev
,再将cur
赋值给prev
再将next_node
赋值给cur
。最后在将prev
返回
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* cur = head;
struct ListNode* prev = NULL;
struct ListNode* nextNode = NULL;
while (cur) {
nextNode = cur->next; // 保存下一个节点
cur->next = prev; // 反转当前节点的指针
prev = cur; // 移动 prev 到当前节点
cur = nextNode; // 移动 cur 到下一个节点
}
return prev; // prev 现在是新的头节点
}
3. 查找链表的中间节点
题目链接 : 查找链表的中间节点
思路 : 利用两个指针,一个指针每次只走一步,另一个指针每次走两步。当走两步的指针走到尾时,走一步的指针就刚好在中间。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
if(head == NULL){
return NULL;
}
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
4. 返回倒数第k个节点
题目链接 : 返回倒数第k个节点
思路 : 定义两个指针 ( fast 、 slow )。 fast先走k-1步,如果
fast->next
不为空,则 slow 和 fast 同时走一步直到fast->next == NULL
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k) {
struct ListNode* slow = head, *fast = head;
int i = 0;
while(i < k-1){
fast = fast->next;
if(fast == NULL){ // k大于有效节点个数的情况
return -1;
}
i++;
}
while(fast->next != NULL){
fast = fast->next;
slow = slow->next;
}
return slow->val;
}
5. 合并两个有序链表
题目链接 : 合并两个有序链表
思路 : 依次比较, 取小的尾插
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
struct ListNode* l1 = list1;
struct ListNode* l2 = list2;
struct ListNode* head = NULL;
struct ListNode* tail = NULL;
// 若 l1 是空的情况,直接返回list2
if(l1 == NULL){
return l2;
}
// 若 l2 是空的情况,直接返回list1
if(l2 == NULL){
return l1;
}
// 循环,条件是两者都不为空继续,一个为空则结束
while(l1 && l2){
// 如果 l1的值大于等于l2
if(l1->val >= l2->val){
// 这个很重要! 要先判断我们的头是否为空,是空就初始化一下
if(head == NULL){
head = l2;
tail = l2;
l2 = l2->next; // 将 l2 的指向移动到下一个节点
}
// head不为空
else{
tail->next = l2;
l2 = l2->next;
tail = tail->next;
}
}
// l1的概念与l2一样
else{
if(head == NULL){
head = l1;
tail = l1;
l1 = l1->next;
}
else{
tail->next = l1;
l1 = l1->next;
tail = tail->next;
}
}
}
// 假设循环结束后l1仍有节点,则直接将l1整体尾插到tail后
// 如: 循环结束后 l1还有两个节点 2->3->NULL
// 那直接尾插就相当于在当前的tail后插入 tail->2->3->NULL (tail取决于循环排序后的最后一个节点是什么)
if(l1){
tail->next = l1;
}
// 同上
if(l2){
tail->next = l2;
}
// 最后返回head
return head;
}
6. 链表分割
题目链接 : 链表分割
思路1 : 大于等于 x 的一个新链表,小于 x 的一个新链表,在小于x的新链表的最后一个节点后插入大于等于x的新链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
// 思路 : 大于等于x的一个新链表,小于 x 的一个新链表
// 在小于x的新链表的最后一个节点后插入大于等于x的新链表
struct ListNode* partition(struct ListNode* head, int x) {
if (head == NULL) {
return head;
}
struct ListNode* cur = head;
struct ListNode *newnode1 = NULL, *tail1 = NULL;
struct ListNode *newnode2 = NULL, *tail2 = NULL;
while (cur) {
if (cur->val >= x) {
if (newnode1 == NULL) {
newnode1 = tail1 = cur;
cur = cur->next;
} else {
tail1->next = cur;
tail1 = tail1->next;
cur = cur->next;
}
} else {
if (newnode2 == NULL) {
newnode2 = tail2 = cur;
cur = cur->next;
} else {
tail2->next = cur;
tail2 = tail2->next;
cur = cur->next;
}
}
}
// 假设newnode1为空 ( 也就是所有值都小于x )
if(newnode1 == NULL){
return newnode2;
}
// 所有值都大于 x 的情况
if(newnode2 == NULL){
return newnode1;
}
tail2->next = newnode1;
tail1->next = NULL;
return newnode2;
}
思路 2 : 与第一种思路相同,但是采用哨兵位的方式做。好处是可以不用处理这么多情况 ( 所有值都大于x 或 所有值小于x 等等 )
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* partition(struct ListNode* head, int x) {
struct ListNode* cur = head;
struct ListNode* LessHead, *LessTail;
struct ListNode* GreaterHead,*GreaterTail;
// 哨兵位头节点
LessHead = LessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
GreaterHead = GreaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
GreaterTail->next = LessTail->next = NULL;
while(cur){
if(cur->val >= x){
GreaterTail->next = cur;
GreaterTail = GreaterTail->next;
}
else{
LessTail->next = cur;
LessTail = LessTail->next;
}
cur = cur->next;
}
LessTail->next = GreaterHead->next;
GreaterTail->next = NULL;
cur = LessHead->next;
free(LessHead);
free(GreaterHead);
return cur;
}
7. 链表的回文结构
题目链接 : 链表的回文结构
思路 : 先找中间节点,从中间节点开始,对后半链表进行反转,再和前半段链表进行比较
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
if(head == NULL){
return NULL;
}
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* cur = head;
struct ListNode* prev = NULL;
struct ListNode* nextNode = NULL;
while (cur) {
nextNode = cur->next; // 保存下一个节点
cur->next = prev; // 反转当前节点的指针
prev = cur; // 移动 prev 到当前节点
cur = nextNode; // 移动 cur 到下一个节点
}
return prev; // prev 现在是新的头节点
}
bool isPalindrome(struct ListNode* head){
struct ListNode* mid = middleNode(head);
struct ListNode* rhead = reverseList(mid);
while(head && rhead){
if(head->val != rhead->val){
return false;
}
head = head->next;
rhead = rhead->next;
}
return true;
}
8. 相交链表
题目链接 : 相交链表
思路 :
- 先求两个链表的长度
- 让长的链表先走两个链表长度的差值步
- 然后两个链表同时走,第一个地址一样的节点就是相交的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* curA,*tailA;
struct ListNode* curB,*tailB;
curA = tailA = headA;
curB = tailB = headB;
int countA = 0,countB = 0;
// 找尾
while(tailA->next != NULL){
countA++;
tailA = tailA->next;
}
while(tailB->next != NULL){
countB++;
tailB = tailB->next;
}
// 判断尾节点是否相等,如果不相等 ( 没有相交的点 直接返回空,反之找相交的点 )
if(tailA != tailB){
return NULL;
}
// 先找哪个链表长度比较长,针对长的那个先让它走两个链表长度差的步数
if(countA != countB){
if(countA > countB){
countA -=countB;
while(countA--){
curA = curA->next;
}
}
else{
countB -= countA;
while(countB--){
curB = curB->next;
}
}
}
// 接着让两个链表同时走,找到相同地址的第一个节点就是相交节点
while(curA && curB){
if(curA != curB){
curA = curA->next;
curB = curB->next;
}
else{
break;
}
}
return curA;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* curA,*tailA;
struct ListNode* curB,*tailB;
curA = tailA = headA;
curB = tailB = headB;
int lenA = 0,lenB = 0;
// 找尾
while(tailA->next != NULL){
lenA++;
tailA = tailA->next;
}
while(tailB->next != NULL){
lenB++;
tailB = tailB->next;
}
// 判断尾节点是否相等,如果不相等 ( 没有相交的点 直接返回空,反之找相交的点 )
if(tailA != tailB){
return NULL;
}
struct ListNode* longlist = headA, *shortlist = headB;
int gap = abs(lenA-lenB);
if(lenA < lenB){
longlist = headB;
shortlist = headA;
}
while(gap--){
longlist = longlist->next;
}
while(longlist && shortlist){
if(longlist != shortlist){
longlist = longlist->next;
shortlist = shortlist->next;
}
else{
break;
}
}
return longlist;
}
9. 环形链表
题目链接 : 环形链表
思路 :
快慢指针,慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行
如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode* slow, *fast;
slow = fast = head;
while(slow && fast && fast->next != NULL){
fast = fast->next->next;
slow = slow->next;
if(fast == slow){
return true;
}
}
return false;
}
延伸问题:
为什么快指针走两步,慢指针走一步的方法可行?
假设环存在,当慢指针进环时没有和快指针碰上,则快指针就会开始追击慢指针。N快指针每次走两步;慢指针每次走一步。也就是说,两个指针进入环后每次的距离都会缩减一步直到两个指针碰面。因此快指针走两步,慢指针走一步的方法是可行的。
快指针一定只能是走两步吗? 可不可以快指针是大于两步的数值,但慢指针仍是一步?
答案是不可行的,以三步为例。当慢指针走一步,快指针走三步,假设环存在,则在环中,两个指针每次的距离都会缩减两步
两个指针的距离变化大概是这样
N N-2 N-4 ... 可以减到0吗? 如果N是偶数,则可以,N是奇数就不行 当快指针走的步数大于2的话,都是有可能追不上的 除非同时控制块慢指针,让他们每次的距离都是缩减2步,就能追上
10. 环形链表II
题目链接 : 环形链表II
思路 : 让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇
证明 :
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* detectCycle(struct ListNode* head) {
struct ListNode *mmet, *slow, *fast;
meet = slow = fast = head;
while (fast && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
break;
}
}
if (fast == NULL || fast->next == NULL) {
return NULL;
}
while (meet != slow) {
meet = meet->next;
if (meet == slow) {
break;
} else {
slow = slow->next;
}
}
return meet;
}
思路2 : 在相遇点断开,再让两个链表求交点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* curA,*tailA;
struct ListNode* curB,*tailB;
curA = tailA = headA;
curB = tailB = headB;
int countA = 0,countB = 0;
// 找尾
while(tailA->next != NULL){
countA++;
tailA = tailA->next;
}
while(tailB->next != NULL){
countB++;
tailB = tailB->next;
}
// 判断尾节点是否相等,如果不相等 ( 没有相交的点 直接返回空,反之找相交的点 )
if(tailA != tailB){
return NULL;
}
// 先找哪个链表长度比较长,针对长的那个先让它走两个链表长度差的步数
if(countA != countB){
if(countA > countB){
countA -=countB;
while(countA--){
curA = curA->next;
}
}
else{
countB -= countA;
while(countB--){
curB = curB->next;
}
}
}
// 接着让两个链表同时走,找到相同地址的第一个节点就是相交节点
while(curA && curB){
if(curA != curB){
curA = curA->next;
curB = curB->next;
}
else{
break;
}
}
return curA;
}
struct ListNode* detectCycle(struct ListNode* head) {
struct ListNode *meet, *slow, *fast;
meet = slow = fast = head;
while (fast && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
//求list1和list2的交点
meet = slow;
struct ListNode* list1 = meet->next;
struct ListNode* list2 = head;
fast->next = NULL;
return getIntersectionNode(list1,list2);
}
}
return NULL;
}