Index LeetCode
(单)链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一节点的指针,因此很多链表问题可以用递归来处理,不同于数组,链表不能直接获取任意节点的值,必须要通过指针找到该节点后才能获取其值。在未遍历到链表结尾时,我们也无法知道链表的长度,除非依赖其他数据存储长度,链表表示方法如下
struct ListNode{
int val;
ListNode *next;
ListNode(int x):val(x),next(nullptr){}
};
由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一节点而非当前节点本身,二是建立一个虚拟节点(dummy node),使其指向当前链表的头节点,这样即使原链表所有节点全部删除,也会有一个dummy存在,返回dummy->next即可。
leetcode中的head节点一般就是a1的地址,a1的数值为head->val
链表的操作一般就是对指针进行修改,一般很少关注数值val,除非排序,把指针当成一个int临时变量看待就行
一般来说,算法题不需要删除内存。在刷LeetCode的时候,如果想要删除一个节点,可以直接进行指针操作而无需回收内存。实际做软件工程中,对于无用的内存,建议尽量显示回收,或利用智能指针。
常用方法:反转迭代、头插法、递归、快慢指针、哈希、归并
BM1.反转链表(206.反转链表)[easy]
反转链表:链表“指针”翻转
输入:1->2->3->4->5->null
输出:null<-1<-2<-3<-4<-5
双指针迭代法
ListNode* reverseList(ListNode* head){
ListNode *pre = nullptr, *cur=head; //反转前需要前节点和当前节点
while(cur != nullptr){
ListNode* temp=cur->next; //用temp,不要用head
cur->next=pre; //双指针迭代
pre=cur;
cur=temp;
}
return pre; //当前节点cur为null,左节点pre就是翻转后的头节点
}
BM2.链表内指定区间反转(92.反转链表II)[medium]
输入:1->2->3->4->5->null,left=2,right=4
输出:1->4->3->2->5->null
双指针头插法
ListNode* reverseBetween(ListNode* head, int left, int right){
ListNode* dummyNode=new ListNode(-1); //加个表头dummyNode,这类问题的一般做法
dummyNode->next=head;
ListNode* pre=dummyNode; //前序节点
ListNode* cur=head; //当前节点
for(int i=0; i<left-1; i++){ //找到left处的pre
pre=cur;
cur=cur->next;
}
for(int i=left; i<right; i++){ //头插法插入节点
ListNode* temp=cur->next;
cur->next=temp->next;
temp->next=pre->next;
pre->next=temp;
}
return dummyNode->next;
}
BM3.链表中的节点每k个一组翻转(25.K个一组翻转链表)[hard]
输入:1->2->3->4->5->null k=2
输出:2->1->4->3->5->null
k=3 :3->2->1->4->5->null
top101-p9,使用递归的方法,使每段链表链接起来很自然
ListNode* reverseKGroup(ListNode* head, int k){ //递归
ListNode* tail=head; //找到每次反转的尾部
for(int i=0; i<k; i++){
if(tail==nullptr) //如果不足k次到了尾链表,直接返回,不反转
return head;
tail=tail->next;
}
ListNode* pre=nullptr; //反转前需要前序节点和当前节点
ListNode* cur=head;
while(cur!=tail){ //在到达当前段尾节点前
ListNode* temp=cur->next; //反转
cur->next=pre;
pre=cur;
cur=temp;
}
head->next=reverseKGroup(tail, k); //当前尾指向下一段要反转的链表
return pre;
}
BM4.合并两个排序的链表(21.合并两个有序链表)[easy]
输入:l1=[1,2,3],l2=[1,3,4]
输出:[1,1,2,3,4,4]
ListNode* mergeTwoLists(ListNode * list1, ListNode* list2){
if(list1==nullptr) return list2;
if(list2==nullptr) return list1;
ListNode* head=new ListNode(-1), *dummy=head;
while(list1 && list2){
if(list1->val > list2->val){
dummy->next=list2;
list2=list2->next;
}else{
dummy->next=list1;
list1=list1->next;
}
dummy=dummy->next;
}
dummy->next=list1? list1:list2;
return head->next;
}
BM5.合并k个已排序链表(23.合并k个升序链表)[hard]
输入:lists=[[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
merge2Lists()
ListNode* mergeKList(vector<ListNode*>& lists){
if(lists.size()==0) return nullptr;
/* 这样也可以,但是合并次数太多,我们可以利用归并排序中的两两合并,减少合并次数
ListNode* ans=lists[0];
for(int i=1; i<lists.size(); i++){
ans=merge2Lists(ans, lists[i]);
}
return ans;*/
return divideMerge(lists, 0, lists.size());
}
ListNode* divideMerge(vector<ListNode*>& lists, int left, int right){
if(left > right){
return nullptr;
}else if(left == right){ //中间一个的情况
return lists[left];
}else{
int mid=(left+right)/2;
return merge2List(divideMerge(lists, left, mid), divideMerge(lists, mid+1, right));
}
}
ListNode* merge2Lists(ListNode* l1, ListNode* l2){ //21.合并两个有序链表
if(l1==nullptr) return l2;
if(l2==nullptr) return l1;
ListNode* dummy=new ListNode(-1), *head=dummy;
while(l1 && l2){
if(l1->val > l2->val){
head->next=l2;
l2=l2->next;
}else{
head->next=l1;
l1=l1->next;
}
head=head->next;
}
head->next=l1? l1:l2;
return dummy->next;
}
BM6.判断链表是否有环(141.环形链表)[easy]
输入:链表头节点head
输出:有环true,无环false
快慢指针、哈希
bool hasCycle(ListNode* head){ //哈希
unordered_set<ListNode*> hash;
while(head){
if(hash.count(head)) return true;
hash.insert(head);
head=head->next;
}
return false;
}
bool hasCycle(ListNode* head){ //快慢指针
ListNode *fast=head, *slow=head;
while(fast && slow){
slow=slow->next;
if(fast->next==nullptr) return false;
fast=fast->next->next;
if(fast==slow) return true;
}
return false;
}
BM7.链表中环的入口结点(142.环形链表II)[medium]
输入:链表头节点head
输出:环形链表的相交节点或null
快慢指针、哈希
ListNode* detectCycle(ListNode *head){ //快慢指针和环形链表基本一样
if(head==nullptr) return head;
ListNode* slow=head, *fast=head;
while(fast != nullptr){
slow=slow->next;
if(fast->next==nullptr) return nullptr;
fast=fast->next->next;
if(fast==slow){
ListNode* ptr=head;
while(ptr!=slow){ //主旨思想:2-1=1
ptr=ptr->next;
slow=slow->next;
}
return ptr;
}
}
return nullptr;
}
BM8.链表中倒数最后k个结点(剑指Offer22.链表中倒数第k个节点)[easy]
输入:1->2->3->4->5,和k=2
输出:4->5
快慢指针
ListNode* getKthFromEnd(ListNode* head, int k){
if(head==nullptr) return head;
ListNode *slow=head, *fast=head;
for(int i=0; i<k; i++){
if(fast==nullptr) return nullptr;
fast=fast->next;
}
while(fast){
slow=slow->next;
fast=fast->next;
}
return slow;
}
BM9.删除链表的倒数第n个结点(19.删除链表中倒数第N个结点)[medium]
输入:head=[1,2,3,4,5],n=2
输出:[1,2,3,5]
---------------------快慢指针,注意头节点删除问题---------------------
ListNode* removeNthFromEnd(ListNode* head, int n){
if(head==nullptr) return head;
ListNode* slow=head, *fast=head;
for(int i=0; i<n; i++){
fast=fast->next;
}
if(fast==nullptr) return head->next; //头节点删除问题
while(fast->next){
slow=slow->next;
fast=fast->next;
}
slow->next=slow->next->next;
return head;
}
BM10.两个链表的第一个公共结点(剑指Offer52.两个链表的第一个公共结点)[easy]
输入:headA,headB
输出:node or null
哈希或双指针
ListNode *getIntersectionNode(ListNode *headA, *headB){ //哈希
if(!headA || !headB) return nullptr;
unordered_set<ListNode*> hash;
while(headA){
hash.insert(headA);
headA=headA->next;
}
while(headB){
if(hash.count(headB)) return headB;
headB=headB->next;
}
return nullptr;
}
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB){ //双指针
if(!headA || !headB) return nullptr;
ListNode *l1=headA, *l2=headB;
while(l1 || l2){
if(l1==l2) return l1;
l1=l1 ? l1->next:headB;
l2=l2 ? l2->next:headA;
}
return nullptr;
}
BM11.链表相加(二)(445.两数相加II)[medium]
输入:[9,3,7],[6,3]
输出:{1,0,0,0}
如果链表不是逆序首先反转链表,逆序相加,变量carry、sum
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2){
if(l1==nullptr) return l2;
if(l2==nullptr) return l1;
ListNode* dummy=new ListNode(-1), *head=dummy;
ListNode *r1=reverseList(l1);
ListNode *r2=reverseList(l2);
int sum=0, carry=0;
while(r1 || r2){
sum=carry;
if(r1){
sum+=r1->val;
r1=r1->next;
}
if(r2){
sum+=r2->val;
r2=r2->next;
}
carry=sum/10;
head->next=new ListNode(sum%10);
head=head->next;
}
if(carry) head->next=new ListNode(1);
return reverseList(dummy->next);
}
ListNode* reverseList(ListNode* head){
if(head==nullptr) return head;
ListNode *pre=nullptr, *cur=head;
while(cur){
ListNode* temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
}
BM12.单链表的排序(148.排序链表)[medium]
输入:head=[-1,5,3,4,0]
输出:[-1,0,3,4,5]
归并排序,合并两个有序链表top101-p38
ListNode* merge(ListNode* l1, ListNode* l2){
if(l1==nullptr) return l2;
if(l2==nullptr) return l1;
ListNode* dummy=new ListNode(-1), *head=dummy;
while(l1 && l2){
if(l1->val > l2->val){
head->next=l2;
l2=l2->next;
}else{
head->next=l1;
l1=l1->next;
}
head=head->next;
}
head->next=l1? l1:l2;
return dummy->next;
}
ListNode* sortInList(ListNode* head){
if(head==nullptr || head->next==nullptr) //空链表或只有一个元素的链表就是有序的
return head;
ListNode* left=head, *mid=head->next, *right=head->next->next;
while(right!=nullptr && right->next!=nullptr){ //右边指针到达末尾时,中间的指针指向该段链表的中间
left=left->next;
mid=mid->next;
right=right->next->next;
}
left->next=nullptr; //从这里断开
return merge(sortInList(head), sortInList(mid)); //分成两段排序,合并排好序的两段
}
BM13.判断一个链表是否为回文结构(234.回文链表)[easy]
输入:1->2->2->1,1->2
输出:true,false
链表数据存到数组里、快慢指针找中点,后半段反转,前后比较
bool isPalindrome(ListNode* head){
if(head==nullptr || head->next==nullptr) return true;
vector<int> nums;
while(head){
nums.push_back(head->val);
head=head->next;
}
int l=0, r=nums.size()-1;
while(l<r){
if(nums[l++]!=nums[r--]) return false;
}
return true;
}
BM14.链表的奇偶重排(328.奇偶链表)[medium]
输入:head=[1,2,3,4,5]
输出:[1,3,5,2,4]
双指针
ListNode* oddEvenList(ListNode* head){
if(head==nullptr) return head;
ListNode *even=head->next; //even开头指向第二个节点,可能为空
ListNode *odd=head; //odd开头指向第一个节点
ListNode *evenhead=even; //指向even开头
while(even != nullptr && even->next != nullptr){
odd->next=even->next; //odd连接even的后一个,即奇数位
odd=odd->next; //odd进入后一个奇数位
even->next=odd->next; //even连接后一个奇数位的后一位,即偶数位
even=even->next; //even进入后一个偶数维
}
odd->next=evenhead; //even整体接在odd后面
return head;
}
BM15.删除有序链表中重复的元素-I(83.删除排序链表中重复的元素)[easy]
输入:1->1->2
输出:1->2
迭代
ListNode* deleteDuplicates(ListNode* head){
if(head==nullptr) return head;
ListNode *dummy=head;
while(head!=nullptr && head->next!=nullptr){
if(head->val == head->next->val){
head->next=head->next->next;
}else{
head=head->next;
}
}
return dummy;
}
BM16.删除有序链表中重复的元素-II(82.删除排序链表中的重复元素II)[medium]
输入:1->2->3->3->4->4->5
输出:1->2->5 删除原始链表中所有重复的数字,只留下不同的数字。返回已排序的链表
ListNode* deleteDuplicates(ListNode* head){
if(head==nullptr) return head;
ListNode *dummy=new ListNode(-1);
dummy->next=head; //在链表前加一个表头
ListNode* cur=dummy;
while(cur->next!=nullptr && cur->next->next!=nullptr){
if(cur->next->val == cur->next->next->val){ //如果遇到相同节点
int temp=cur->next->val;
while(cur->next!=nullptr && cur->next->val==temp){
cur->next=cur->next->next;
}else{
cur=cur->next;
}
}
}
return dummy->next; //返回时去掉表头
}