以下题目均为IO型。
1.给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点
题目示例如上:
解题思路:
双指针问题,给定指针prev和cur,从头结点开始往后遍历,分两种情况讨论:
- 假如头节点为要删除的节点
- 假如中间节点为要删除的节点
prev记录要删除节点的前一个节点,cur记录要删除的节点
假如是第一种情况,cur->val == val时,
if(cur == head)//1.头删 { head = cur->next; free(cur); cur = head; }
假如是第二种情况,
prev->next = cur->next; free(cur); cur = prev->next;
完整代码如下:
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ //画图分析才会写 typedef struct ListNode ListNode; struct ListNode* removeElements(struct ListNode* head, int val) { ListNode*cur = head; ListNode*prev = head; while(cur) { if(cur->val==val) { if(cur == head)//1.头删 { head = cur->next; free(cur); cur = head; } else//2.中间删除 { prev->next = cur->next; free(cur); cur = prev->next; } } else { prev = cur; cur = cur->next; } } return head; }
2.给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
反转链表较为简单,下面介绍两种方法:
- 三指针遍历法
给定三个指针prev为NULL,cur = head,next = cur->next,这样往后遍历即可。
typedef struct ListNode ListNode; 法1,三指针往后迭代,prev,cur,next往后,画图才会写 struct ListNode* reverseList(struct ListNode* head) { ListNode*prev = NULL; ListNode*cur = head; while(head) { cur = cur->next; head ->next = prev; prev = head; head = cur; } return prev; }
typedef struct ListNode ListNode; 法2,头插 struct ListNode* reverseList(struct ListNode* head) { ListNode*newhead = NULL; ListNode*cur = head; while(cur) { ListNode*next = cur->next; //头插 cur->next = newhead; newhead = cur; //迭代往后 cur = next; } return newhead; }
3.给定一个头结点为 head 的非空单链表,返回链表的中间点。如果有两个中间结点,则返回第二个中间结点。
只需要使用两个指针,fast和slow指针即可,快指针一次走两步,慢指针一次走一步,这样快指针走完时,慢指针一定走到中间节点。
//最优解:快慢指针,慢指针一次走一步,快指针一次走两步 struct ListNode* middleNode(struct ListNode* head) { struct ListNode*slow = head,*fast = head; while(fast && fast->next) { fast = fast->next->next; slow = slow->next; } return slow; }
4.输入一个链表,输出该链表中倒数第k个结点。
解题思路:仍然使用快慢指针:但是这样的快慢指针不是像上一道题快指针走两步,慢指针走一步。
这里的快慢指针是快指针先走,慢指针后走。
fast先走k步,然后fast和slow同时走。
当fast == NULL时,slow即为倒数第k个
原因:快指针先走的k步,消除了倒数的概念。
typedef struct ListNode ListNode; struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) { ListNode*fast = pListHead,*slow = pListHead; int count =0; while(count <k) { if(fast == NULL) { return NULL; } fast = fast ->next; count++; } while(fast) { slow = slow->next; fast = fast->next; } return slow; }
5.将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
解题思路:
使用带哨兵位的头节点。2。不使用带哨兵位的头节点
使用带哨兵位的头节点时,好处在于不需要将最小的节点作为头节点,劣势在于由于哨兵位的头节点是malloc出来的,使用完之后需要释放,在释放之间还需要保存哨兵位的头节点的next。
哨兵位的头节点的好处是,不需要再把第一个小的值给head typedef struct ListNode ListNode; struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) { 带哨兵位的头解法: if(list1==NULL) return list2; if(list2 == NULL) return list1; //带哨兵位的头节点 ListNode*head = NULL; head = (ListNode*)malloc(sizeof(ListNode)); ListNode*tail = head; while(list1 && list2) { if(list1->val < list2->val) { tail->next = list1; tail = list1; list1 = list1->next; } else { tail->next = list2; tail = list2; list2 = list2->next; } } if(list1) { tail->next = list1; } if(list2) { tail->next = list2; } //注意这里,不能用prevhead = head,然后return prevhead->next //因为prev = head,head已经free了,相当于prev非法访问了。 //所以只能prev = head->next,return prev; ListNode*prevhead = head->next; free(head); return prevhead; }
6.现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
举个简单的例子:
上面的链表是原链表,定值x是4,也就是小于4的节点需要在前面,大于4的节点在后面,并且不能改变原来链表的节点的顺序。下面的链表即为题目要求的链表。
注意:
由于原链表中的6的next仍然指向2,会造成出现环形链表的情况,会造成死循环,所以需要将6的next 置为NULL。
思路:将小于定值x和大于定值x的链表分开,分别连接后,最后在将小的链表和大的链表连接起来。
class Partition { public: ListNode* partition(ListNode* pHead, int x) { // write code here struct ListNode*LessHead ,*LessTail,*GreaterHead,*GreaterTail; LessHead=LessTail=GreaterHead=GreaterTail= NULL; LessTail = LessHead= (struct ListNode*)malloc(sizeof(struct ListNode)); GreaterTail= GreaterHead = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* cur = pHead; while (cur) { if (cur->val < x) { LessTail->next = cur; LessTail = cur; } else { GreaterTail->next = cur; GreaterTail = cur; } cur = cur ->next; } //到这里说明cur走完链表了,此时需要将两个链表连接起来 LessTail->next = GreaterHead->next; //注意:一定要将GreaterTail->next = NULL //假如链表为2->3->5->7->6->2,GreaterTail->next还指向2 //造成链表成环形了,所以要置空 GreaterTail->next = NULL; ListNode* newnode = LessHead->next; free(LessHead); free(GreaterHead); return newnode; } };
7.
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
思路:所谓的回文结构,其实就是判断链表是否对称。
- 先找出链表的中间节点,然后以该中间节点为界,将链表分割。
- 前面的链表称为list1,后面的链表称为list2。
将list2逆置,再将list1和list2的节点逐个判断,如果list1或者list2的节点都相等,那就是回文结构。
//链表回文的思路: // 1、先找出链表的中间节点,(偶数个找第二个中间节点) // 2.中间节点作为新的链表头进行逆置。 // 将新逆置后的链表和原链表的val相比,(因为原链表的某些节点的next存储着新链表的某些节点的地址) ListNode* MidListNode(ListNode* A) { ListNode*slow =A,*fast = A; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; } ListNode* reverseListNode(ListNode* mid) { ListNode*prev = NULL; ListNode*cur = mid; while(cur) { ListNode*pnext = cur->next; cur->next = prev; prev = cur; //迭代往后 cur = pnext; } return prev; } class PalindromeList { public: bool chkPalindrome(ListNode* A) { // write code heretypedef struct ListNode ListNode; // write code here ListNode*mid = MidListNode(A); ListNode*rhead = reverseListNode(mid); ListNode*curA = A; ListNode*curR = rhead; while(curA && curR) { if(curA->val!=curR->val) { return false; } else { curA = curA->next; curR = curR->next; } } return true; } };
8.给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
思路:给定一个fast指针和slow指针,先分别遍历两个链表,记录长度,让fast指针指向长链表,fast指针先走 长链表长度-链表长度,随后slow指针从短链表的头开始,和fast链表一起往后走,每个指针一次走一步,如果相等,则相等的点为相交点。
原因:快指针先走的长度差消除了长短链表的长度差。
typedef struct ListNode ListNode; struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) { ListNode*curA = headA; ListNode*curB = headB; int i = 0; int j = 0; //先记录两个链表的长度,随后让长链表先走k步,再一起走,就消除了长度差,时间复杂度为O(N),空间复杂度为O(1); //还可以再遍历完两个链表的同时,记录两个链表的尾结点,如果尾结点不相同,那肯定不相交,直接返回NULL ListNode*TailA = NULL; ListNode*TailB = NULL; while(curA) { ++i; TailA = curA; curA = curA->next; } while(curB) { ++j; TailB = curB; curB = curB->next; } if(TailA !=TailB) { return NULL; } if(i>j) { int k = i-j; curA = headA; curB = headB; while(k--) { curA = curA->next;//长链表先走k步 } while(curB && curA) { if(curA == curB) { return curA; } else { curA = curA->next; curB = curB->next; } } } else { int k = j-i; curA = headA; curB = headB; while(k--) { curB = curB->next;//长链表先走k步 } while(curB && curA) { if(curA == curB) { return curA; } else { curA = curA->next; curB = curB->next; } } } return NULL; }