Linked List相关的题

237. Delete Node in a Linked List

这题跟正常的书里的删链表某个节点区别是传入的参数,书里函数输入是要删的那个节点的前一个节点,这题里给的函数输入就是要删的那个节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        node->val = node->next->val;
        node->next = node->next->next;
    }
};

书里的操作是把node-next = node->next->next,这题思路就是把当前的这个节点变成后面的节点,然后再删后面的节点。


203. Remove Linked List Elements

Remove all elements from a linked list of integers that have value val.

Example
Given: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6
Return: 1 --> 2 --> 3 --> 4 --> 5


常规思路就是造个dummy节点,保证head节点就是val时,也可以删掉。


解法1:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode dummy(0);//随便初始化个值就行,反正也不用值,只用dummy的next指针
        dummy.next = head;
        head = &dummy;
        while (head->next) {
            if (head->next->val == val) {
                head->next = head->next->next;
            } else {
                head = head->next;
            }
        }
        return dummy.next;
    }
};
这个就是造个dummy节点,把dummy.next指向head,然后再把head移到dummy处。之后每次检测val值时,移动head就ok了,相当于把head当成巡逻兵去一个一个排查。


解法2:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode** dummy=&head;

        while(*dummy){
            if((*dummy)->val==val){
                *dummy=(*dummy)->next;
            }else
                dummy=&((*dummy)->next);
        }
        return head;
    }
};
这个就是造个节点指向head,然后把造的这个节点当成巡逻兵去一个一个排查。


解法3:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        if (head == NULL) return NULL;
        head->next = removeElements(head->next, val);
        return head->val == val ? head->next : head;
    }
};

没啥用,遇到超大链表,占用空间就很大了。


83. Remove Duplicates from Sorted List

Given a sorted linked list, delete all duplicates such that each element appear only once.

For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.


这题头结点不会被删掉,不用考虑头结点处理的问题,造一个能保存链表原始头的指针就行。核心就是判断当前节点的value和后一个节点额value值是否相等,要是相等,就删了后一个节点;要是不想等,就向后移动head(把head当成巡逻兵)。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head) return NULL;
        ListNode* dummy = head;
        while(head->next) //要保证后面用到的取节点内value的指针不为空
        {
            if(head->val==head->next->val) 
                head->next = head->next->next;
            else
                head = head->next;
        }
        return dummy;
    }
};

82. Remove Duplicates from Sorted List  II

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.


这题得考虑一下头结点,头结点有可能被删掉。

1. 因此要建一个dummy节点,把dummy链上head。

2. 在判断下一节点的value和当前节点的value是否相等的同时,要先判断下一个节点是否为NULL,不然在你取下一个节点的value时就会出错。

3. 因为遇到value相等的时候,不仅要删掉后面的节点,还要把当前节点也删掉。因此,需要两个巡逻兵节点。一个巡逻兵负责循环判断当前节点和后一个节点的值是否相等,另一个巡逻兵负责存储第一个巡逻兵的起始位置。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head || !head->next)  return head;
        
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode*pre = dummy;
       
        while(pre->next)
        {
            ListNode*cur = pre ->next;
            bool flag = false;
            while(cur->next && cur->val==cur->next->val)
            {
                cur = cur->next;//这句话写成这样也是ok的:cur->next = cur->next->next;这是第二次做这道题的时候写成这样,脑中debug时没发现问题。提交也Accept
                flag = true;
            }
            if(flag)  //if(pre->next != cur)
                pre->next = cur->next;
            else
                pre = pre->next;
        }
        return dummy->next;
    }
};

19.Remove Nth Node From End of List

Given a linked list, remove the nth node from the end of list and return its head.

For example,

   Given linked list: 1->2->3->4->5, and n = 2.

   After removing the second node from the end, the linked list becomes 1->2->3->5.

Note:
Given n will always be valid.
Try to do this in one pass.


notes说给的n一定是有效的,还让一次遍历就搞定这题。这样就不能统计链表长度再做了。

解决办法:用一前一后两个指针,以n的间隔向后移动。初始时就是前一个节点在head处,后一个节点在向后移动n个位置处。当后一个节点为NULL时,就删pre的next。

边界条件:

1. 链表只有一个元素时,这时n应该是1,n不能为0,否则n就不是有意义的值了。

2. 链表有n个元素时,当初始化时,把cur移动n个位置时,这时cur应该为NULL。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(!head->next) return NULL;
        
        ListNode *pre=head,*cur=head;
        
        for(int i=0;i<n;++i) cur=cur->next;
        if(!cur) return head->next;
        while(cur->next)
        {
            cur=cur->next;
            pre=pre->next;
        }
        pre->next=pre->next->next;
        return head;
    }
};

1 47. Insertion Sort List

Sort a linked list using insertion sort.


就是对单链表排序,要求使用插入排序。

插入排序:(网上大部分的实现逻辑)

1. 用dummy来存新的排好序的链表

2. 用cur来当巡逻兵,在有序链表里巡逻,跟原链表里抽出的元素比较,来定插入dummy链表的位置。

3. 剩下的就是最简单的往链表中插入节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* insertionSortList(ListNode* head) {
        ListNode *dummy = new ListNode(0);
        while(head)
        {
            ListNode *cur = dummy;
            ListNode *next = head->next;
            while(cur->next!=NULL && cur->next->val<head->val)
               cur=cur->next;
            head->next=cur->next;
            cur->next = head;
            head = next;
        }
        return dummy->next;
    }
};

148. Sort List

Sort a linked list in O(n log n) time using constant space complexity.


让使用时间复杂度是O(n log n),空间复杂度是常数的排序算法。


那么就选归并排序吧。而且现在才知道,原来归并排序这么牛逼。


还不会链表的归并排序,等会了再贴代码吧~



206. Reverse Linked List

Reverse a singly linked list.

click to show more hints.

Hint:

A linked list can be reversed either iteratively or recursively. Could you implement both?


用一个dummy节点当新链表的表头,再用一个临时的next节点存head->next。之后就是一个一个往dummy上插了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head) return head;
        
        ListNode *dummy = NULL;
        while(head)
        {
            ListNode *next = head->next;
            head->next = dummy;
            dummy = head;
            head = next;
        }
        return dummy;
    }
};

题目要求用迭代和递归两种方法实现,递归没看懂,先留着这坑,回头填



92. Reverse Linked List II

Reverse a linked list from position m to n. Do it in-place and in one-pass.

For example:
Given 1->2->3->4->5->NULLm = 2 and n = 4,

return 1->4->3->2->5->NULL.

Note:
Given mn satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.

方法一:第一种翻转方式(这里的方法是以翻转方式分的

先考虑头结点会不会被改变?会,那就造个dummy节点接到头结点前面。

剩下的就是再用个指针先指到第m个结点附近,再从m开始,一直翻转到n,最后把三段连起来。

这其中有几个地方需要暂存一下地址。第一段里有个需要存尾巴的seg_1指针,第二段(翻转的那部分)里需要一个记录下一个位置的next指针,一个需要记录翻转后链表的头结点指针seg_2,一个巡逻兵功能的cur指针。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        ListNode *dummy = new ListNode(0);
        ListNode *seg_1,*seg_2,*cur,*next;
        dummy->next = head;
        seg_1 = dummy;
        for(int i=1;i<m;++i) seg_1 = seg_1->next; //循环执行了m-1次,循环结束后,seg_1是第m-1个节点
        cur = seg_1->next;     //当前cur为第m个节点
        for(int i=m;i<=n;++i)
        {
            next = cur->next;
            cur->next = seg_2;
            seg_2 = cur;     //循环结束后,dummy_reverse为翻转的链表头
            cur = next;         //循环结束后,cur为第n+1个节点
        }
        seg_1->next->next = cur;
        seg_1->next = seg_2;

        return dummy->next;
    }
};

这个翻转处的四句话:

首尾两句用于向后遍历,每次循环都把next移到cur的下一个位置处,最后再把被改变了的cur移到next处。

中间两句用于向【存储翻转后的链表的表头插元素,cur为当前从原链表中取出来的节点,先把该节点的next接上【存储翻转后的链表的表头。cur->【存储翻转后的链表的表头

再把【存储翻转后的链表的表头移到此次循环的cur处。此时的表头就是包含cur的翻转后的链表了。


方法二:第二种翻转方式

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        ListNode *cur = dummy;
        ListNode *pre, *front, *last;
        for (int i = 1; i <= m - 1; ++i) cur = cur->next;
        pre = cur;
        last = cur->next;
        for (int i = m; i <= n; ++i) {
            cur = pre->next;
            pre->next = cur->next;
            cur->next = front;
            front = cur;
        }
        cur = pre->next;
        pre->next = front;
        last->next = cur;
        return dummy->next;
    }
};
这个翻转处的代码:
前两句是用于向后遍历,类似删除节点,pre->next=pre->next->next,而中间这个被删除的就是哪来翻转用的节点。
后两句是用于翻转操作把这个cur的后面接上front,再把front移到cur处。

这里的pre指针很妙,在循环的开始到结束,pre的位置始终没变,pre存的是第一段的尾巴;在循环运行的过程中,不断的改变pre->next,在循环结束时,pre-next指到了第三段的头


25. Reverse Nodes in k-Group

Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.

k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.

You may not alter the values in the nodes, only nodes itself may be changed.

Only constant memory is allowed.

For example,
Given this linked list: 1->2->3->4->5

For k = 2, you should return: 2->1->4->3->5

For k = 3, you should return: 3->2->1->4->5

第一种用递归的方法,

先实现子函数(翻转链表),传入参数是:要翻转区间的head和tail->next;返回翻转后的表头。

主函数使用递归,逻辑是:

边界条件是检查当前是否有k个非空节点。如果没有,就终止递归,直接返回当前层输入的head;如果有,那就返回翻转后的链表。

2. 在局部翻转之后,返回翻转结果之前,这时候链表的cur到tail是未被处理的。因此,递归就放在这里调用。这里递归每次返回的是从cur到tail被处理过的链表,需要把这个返回的链表头连接到当前层的tail->next,当前层经过翻转后,head就是tail,这样就把上一层递归返回的链表头节点接到head后面。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        auto cur = head;
        for(int i = 0; i < k; ++ i) {
            if(!node) return head;
            cur = cur->next;
        }
        auto new_head = reverse(head, cur);
        head->next = reverseKGroup(cur, k);
        return new_head;        
    }
    ListNode* reverse(ListNode* start, ListNode* end) {
        ListNode* head = end;
        while(start != end) {
            auto temp = start->next;
            start->next = head;
            head = start;
            start = temp;
        }
        return head;
    }
};

第二种 用迭代的方法

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = pre;
        dummy->next = head;
        int num = 0;
        while (cur = cur->next) ++num;
        while (num >= k) {
            cur = pre->next;
            for (int i = 1; i < k; ++i) {
                ListNode *t = cur->next;
                cur->next = t->next;
                t->next = pre->next;
                pre->next = t;
            }
            pre = cur;
            num -= k;
        }
        return dummy->next;
    }
};
前面几行很常规,因为头结点会变,造个dummy。while再统计以下链表的长度。接着就是下面包含主要操作的while循环。


24. Swap Nodes in Pairs

Given a linked list, swap every two adjacent nodes and return its head.

For example,
Given 1->2->3->4, you should return the list as 2->1->4->3.

Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.


用pre和cur的组合,同时用pre暂存翻转后的链表。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *dummy =new ListNode(0),*pre = dummy,*cur=dummy;
        dummy->next = head;
        
        while(pre->next && pre->next->next)
        {
            cur = pre->next->next;
            pre->next->next = cur->next;
            cur->next = pre->next;
            pre->next = cur;
            pre = cur->next;
        }
        return dummy->next;
    }
};

每次循环就是下面这么折腾的:



下面是递归版本:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode *t = head->next;
        head->next = swapPairs(head->next->next);
        t->next = head;
        return t;
    }
};


61. Rotate List

Given a list, rotate the list to the right by k places, where k is non-negative.


Example:

Given 1->2->3->4->5->NULL and k = 2,

return 4->5->1->2->3->NULL.

俩办法,一个是快慢指针、一个是头尾相连。

快慢指针:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head) return NULL;
        int n = 0;
        ListNode *cur = head;
        while (cur) {
            ++n;
            cur = cur->next;
        }
        k %= n;
        ListNode *fast = head, *slow = head;
        for (int i = 0; i < k; ++i) {
            if (fast) fast = fast->next;
        }
        if (!fast) return head;
        while (fast->next) {
            fast = fast->next;
            slow = slow->next;
        }
        fast->next = head;
        head = slow->next;
        slow->next = NULL;
        return head;
    }
};
先遍历一遍,统计出有多少个节点。然后处理一下k,使k不大于n。
快指针向后走k个位置,来到n(k+1)处。【这里有个边界条件,k=n时,所以在结束for循环后再判断一下head是否为NULL】

接着把慢指针和快指针一起走,当快指针向后走到尾巴时,慢指针就到了节点n-k-1处。

把head接到尾巴上,head移到慢指针的next处,慢指针所在的节点next置成NULL。


头尾相连:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head) return NULL;
        int n = 1;
        ListNode *cur = head;
        while (cur->next) {
            ++n;
            cur = cur->next;
        }
        cur->next = head;
        k = n - k % n;
        for (int i = 0; i < k; ++i) {
            cur = cur->next;
        }
        head = cur->next;
        cur->next = NULL;
        return head;
    }
};
先遍历到链表最后一个节点,然后跟头接到一起。把k对n求余,防止超出n的范围。

然后从结尾向后移动n-k次,下一个节点就是从后向前第k个节点。

把head移到cur->next处,再把cur后面指向NULL就ok啦。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值