LeetCode -- LinkList

目录

237. Delete Node in a Linked List

206. Reverse Linked List

21. Merge Two Sorted Lists

83. Remove Duplicates from Sorted List

203. 删除链表中的节点

234. 回文链表

160. 相交链表

141. 环形链表

109. 有序链表转换二叉搜索树

24. 两两交换链表中的节点

328. 奇偶链表

148. 排序链表

147. 对链表进行插入排序

143. 重排链表

725. Split Linked List in Parts

2. 两数相加

445. 两数相加 II

817. Linked List Components

86. 分隔链表

92. 反转链表 II

82. 删除排序链表中的重复元素 II

61. 旋转链表

142. 环形链表 II

19. 删除链表的倒数第N个节点

138. 复制带随机指针的链表

25. k个一组翻转链表

23. 合并K个排序链表


237. Delete Node in a Linked List

Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.

Given linked list -- head = [4,5,1,9], which looks like following:

    4 -> 5 -> 1 -> 9

Example 1:

Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list
             should become 4 -> 1 -> 9 after calling your function.

Example 2:

Input: head = [4,5,1,9], node = 1
Output: [4,5,9]
Explanation: You are given the third node with value 1, the linked list
             should become 4 -> 5 -> 9 after calling your function.

Note:

  • The linked list will have at least two elements.
  • All of the nodes' values will be unique.
  • The given node will not be the tail and it will always be a valid node of the linked list.
  • Do not return anything from your function.
    void deleteNode(ListNode* node) {
        //思路:只给当前节点,那么不能按便遍历的思路来实现
        //      因为当前节点是要被删除的,可将当前节点的值设置为下一节点的值,然后将下一节点删除即可
        node->val = node->next->val;
        ListNode* tmp = node->next;
        node->next = node->next->next;
        delete(tmp);
    }

 

206. Reverse Linked List

Reverse a singly linked list.

Example:

Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
    ListNode* reverseList(ListNode* head) {
        //思路:每次拿出链表的头节点 插入到新链表最开始处(作为新链表的头节点)
        ListNode* newhead = NULL;
        
        while (head != NULL)
        {
            ListNode* tmp = head;
            head = head->next;
            tmp->next = newhead;
            newhead = tmp; //newhead作为新链表头节点 
        }
        return newhead;
    }

 

21. Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

Example:

Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // 比较节点值大小 并 归并的过程
        if (l1 == NULL)
            return l2;
        if (l2 == NULL)
            return l1;
        ListNode* head = NULL;
        //ListNode* head = new ListNode(0);
        if (l1->val < l2->val)
        {
            head = l1;
            l1 = l1->next;
        }
        else
        {
            head = l2;
            l2 = l2->next;
        }
        
        ListNode* phead = head;
        while (l1 != NULL && l2 != NULL)
        {
            if (l1->val < l2->val)
            {
                phead->next = l1;
                l1 = l1->next;
            }

            else
            {
                phead->next = l2;
                l2 = l2->next;
            }
            phead = phead->next;
        }
        
        //l1先遍历完,将l2接入新链表中
        if (l1 == NULL)
            phead->next = l2;
        
        //l2先遍历完,将l1接入新链表中
        if (l2 == NULL)
            phead->next = l1;
        
        return head;
    }

 

83. Remove Duplicates from Sorted List

 

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

Example 1:

Input: 1->1->2
Output: 1->2

Example 2:

Input: 1->1->2->3->3
Output: 1->2->3
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* phead = head;
        
        if (phead == NULL || phead->next == NULL)
            return head;
        
        while (phead != NULL && phead->next != NULL)
        {
            if (phead->val == phead->next->val)
            {
                ListNode* tmp = phead->next;
                phead->next = tmp->next;
                delete tmp;
            }
            else
            {
                phead = phead->next;
            }
        }
        
        return head;
    }

 

203. 删除链表中的节点

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
ListNode* removeElements(ListNode* head, int val) {
        if (head == NULL)
            return head;
        // 思路:考虑到头节点可能被删除,可在头节点之前添加新的头节点,方便操作
        ListNode* phead = new ListNode(0);
        phead->next = head;
        
        ListNode* pphead = phead;
        while (pphead->next != NULL)
        {
            if (pphead->next->val == val)
            {
                ListNode* tmp = pphead->next;
                pphead->next = tmp->next;
                // 删除的节点应该进行释放
                delete tmp; 
            }
            else
            {
                pphead = pphead->next;
            }
        }
        ListNode* h = phead->next;
        // 最先new的头节点应该释放
        delete phead;
        return h;
    }

 

234. 回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

bool isPalindrome(ListNode* head) {
        if (head == NULL || head->next == NULL)
            return true;
        
        //找到链表的中心:如1->2->2->1 中心为2(第一个2), 1->2->1中心为2
        //快慢指针 *fast, *slow
        ListNode* fast = head;
        ListNode* slow = head;
        
        while (fast->next != NULL && fast->next->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        
        // 将后半段反转, 可调用之前的reverseList函数
        //slow->next = reverseList(slow->next);
        // 这里重新编写代码段, 对back进行反转
        ListNode* back = slow->next;
        ListNode* pre = head;
        
        ListNode* tmp = NULL;
        while (back != NULL)
        {
            ListNode* back_next = back->next;
            back->next = tmp;
            tmp = back;
            back = back_next;;
        }
        
        while (tmp != NULL)
        {
            if (pre->val != tmp->val)
            {
                return false;
            }
      
            pre = pre->next;
            tmp = tmp->next;
        }
        return true;
    }
    
    ListNode* reverseList(ListNode* head) {

        ListNode* newhead = NULL;
        
        while (head != NULL)
        {
            ListNode* tmp = head;
            head = head->next;
            tmp->next = newhead;
            newhead = tmp;  
        }
        return newhead;
    }

 

160. 相交链表

编写一个程序,找到两个单链表相交的起始节点。

例如,下面的两个链表

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

在节点 c1 开始相交。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 先求两个链表的长度差k, 让长链表先走k步,
        // 然后两个链表一起走,判断节点是否相等
        if (headA == NULL || headB == NULL)
            return NULL;
        
        int lenA = 0, lenB = 0;
        ListNode* pa = headA;
        ListNode* pb = headB;
        while (pa != NULL)
        {
            lenA ++;
            pa = pa->next;
        }
        while (pb != NULL)
        {
            lenB ++;
            pb = pb->next;
        }
        
        // 长链表后移k步
        pa = headA;
        pb = headB;
        int k = 0;
        if (lenA > lenB)
        {
            k = lenA - lenB;
            while (k--)
            {
                pa = pa->next;
            }
        }
        else
        {
            k = lenB - lenA;
            while (k--)
            {
                pb = pb->next;
            }
        }
        
        // pa, pb 一起后移,并判断节点是否相等
        while(pa != NULL)
        {
            if (pa != pb)
            {
                pa = pa->next;
                pb = pb->next;
            }
            else
            {
                return pa;
            }
        }
        return NULL;
    }

 

141. 环形链表

给定一个链表,判断链表中是否有环。

进阶:
你能否不使用额外空间解决此题?

bool hasCycle(ListNode *head) {
        // 思路:快慢指针,有环存在,则快指针一定能追上慢指针
        if (head == NULL || head->next == NULL)
            return false;
        
        ListNode* slow = head;
        ListNode* fast = head;
        
        // 保证fast->next->next存在(一定要保证用到的节点存在)
        while (fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast)
            {
                return true;
            }
        }
        return false;
    }

 

109. 有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5
class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        if (head == NULL)  return NULL;
        if (head->next == NULL) return new TreeNode(head->val);
        //快慢指针找链表的中间节点
        ListNode* slow = head;
        ListNode* fast = head;
        int i = 0;
        // 这里slow第一步不走,结束后slow表示的是左半部的最后一个节点
        while (fast != NULL && fast->next != NULL)
        {
            fast = fast->next->next;
            if (i > 0)
            {
                slow = slow->next;
            }
            i++;
        }
        //右半部的头结点
        //将链表分成两半,左半部分的尾部指向NULL
        TreeNode* root = new TreeNode(slow->next->val);
        root->right = sortedListToBST(slow->next->next);
        // 左半部最后节点指向NULL
        slow->next = NULL;
        root->left = sortedListToBST(head);
        return root;
    }
        /* 另外一种解法,将节点的值保存在vector中,可以直接使用索引进行操作
        TreeNode* sortedListToBST(ListNode* head) {
        vector<int> vec;
        ListNode* p = head;
        while (p != nullptr) {
            vec.push_back(p->val);
            p = p->next;
        }
        return helper(vec, 0, vec.size() - 1);
        }
        TreeNode* helper(vector<int>& vec, int left, int right) {
            if (left > right)
                return nullptr;
            int mid = (left + right + 1) / 2;
            TreeNode* node = new TreeNode(vec[mid]);
            node->left = helper(vec, left, mid - 1);
            node->right = helper(vec, mid + 1, right);
            return node;
        }
        */
};

 

24. 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

说明:

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
ListNode* swapPairs(ListNode* head) {
        if (head == NULL || head->next == NULL) return head;
        
        // 先对前两个节点进行交换 
        // 这里phead成为新的头结点,head成为一组(两个一组)中后一个节点
        ListNode* phead = head->next;
        head->next = phead->next;
        phead->next = head;
        
        //将第二个节点保存在second中,并将head后移至下一组
        ListNode* second = head;
        head = head->next;
        
        while (head != NULL && head->next != NULL)
        {
            ListNode* tmp = head->next;
            head->next = tmp->next;
            tmp->next = head;
            second->next = tmp;
            
            second = head;
            head = head->next;
        }
        return phead;
    }

 

328. 奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

示例 2:

输入: 2->1->3->5->6->4->7->NULL 
输出: 2->3->6->7->1->5->4->NULL

说明:

  • 应当保持奇数节点和偶数节点的相对顺序。
  • 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
ListNode* oddEvenList(ListNode* head) {
        //思路:分成两条链表,一条为奇数节点,一条为偶数节点,
        //最后将偶数节点链表接在奇数链表后
        if (head == NULL || head->next == NULL || head->next->next == NULL)
            return head;
        
        ListNode* p1 = head;
        ListNode* p2 = head->next;
        ListNode* pp2 = p2;
        
        while (p2 != NULL && p2->next != NULL)
        {
            p1->next = p2->next;
            p2->next = p1->next->next;
            p1 = p1->next;
            p2 = p2->next;
        }
        p1->next = pp2;
        return head;
    }

 

148. 排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    
    //归并排序思想
    /*
    ListNode* sortList(ListNode* head) {
        //归并的思想:链表分成两段,分别排序,再归并
        if (head == NULL || head->next == NULL)
            return head;
        //快慢指针找到中间节点

        ListNode* slow = head;
        ListNode* fast = head->next;
        while (fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        
        fast = slow->next; //fast后半段的链表头
        slow->next = NULL; // 截断链表 成两段
        //递归排序
        slow = sortList(head);
        fast = sortList(fast);
        
        return merge(slow, fast);
    }
    
    // 归并l1, l2  l1,l2 均为已排好序的链表
private:
    ListNode* merge( ListNode* l1, ListNode* l2 )
    {
        if (l1 == NULL)
            return l2;
        if (l2 == NULL)
            return l1;
        
        ListNode* head = new ListNode(0);
        ListNode* cur = head;
        
        while (l1 != NULL && l2 != NULL)
        {
            if (l1->val <= l2->val)
            {
                cur->next = l1;
                l1 = l1->next;
            }
            else
            {
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }
        if (l1 == NULL)
            cur->next = l2;
        if (l2 == NULL)
            cur->next = l1;
        ListNode* tmp = head->next;
        delete head;
        return tmp;
    }*/
    
    // 快速排序思想
    ListNode* sortList(ListNode* head) {
        
        if (head == NULL || head->next == NULL) {
            return head;
        }
        ListNode* pBegin = head;
        ListNode* pEnd = NULL;
        //while(pEnd->next != NULL){
         //   pEnd = pEnd->next;
        //}
        QuickSort(pBegin, pEnd);
        
        return head;
    }
    void QuickSort(ListNode* pBegin, ListNode* pEnd){
        if (pBegin != pEnd){
            ListNode* v = partition(pBegin, pEnd);
            QuickSort(pBegin, v);
            QuickSort(v->next, pEnd);
        }
    }
    
    //[pBegin+1, p] <key, [p->next, q)
    ListNode* partition(ListNode* pBegin, ListNode* pEnd){
        int key = pBegin->val;
        ListNode* p = pBegin; // 小于key的部分的最后一个
        ListNode* q = p->next;
        
        while (q != pEnd){
            if (q->val < key){
                p = p->next;
                swap(p->val, q->val);
            }
                
            q = q->next;
        }
        swap(p->val, pBegin->val);
        return p;
    }
};

 

147. 对链表进行插入排序

对链表进行插入排序。


插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。
ListNode* insertionSortList(ListNode* head) {
        ListNode* new_head = new ListNode(0);
        new_head->next = head;
        ListNode* pre = new_head; // 用于索引插入的合适位置
        // cur->next 是要拿下来插入到合适位置的节点 
        // 这里不用cur,是方便删除该节点,不然还得知道cur的前一个节点
        ListNode* cur = head;     
        while (cur) {
            if (cur->next && cur->next->val < cur->val) {
                while (pre->next && pre->next->val < cur->next->val)
                    pre = pre->next;
                /* Insert cur -> next after pre.*/
                ListNode* temp = pre->next;
                pre->next = cur->next;
                cur->next = cur->next->next;
                pre->next->next = temp;
                /* Move pre back to new_head. */ 
                pre = new_head;
            }
            else cur = cur->next;
        }
        ListNode* res = new_head->next;
        delete new_head;
        return res;
    }

 

143. 重排链表

给定一个单链表 LL0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→LnL1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3.

示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
void reorderList(ListNode* head) {
        //思路:先翻转后半段作为一个新链表,再合并两条链表
        if (head == NULL || head->next == NULL)
            return ;
        //快慢指针找到中间节点
        ListNode* slow = head;
        ListNode* fast = head->next;
        
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        //slow->next 为后半段头结点
        fast = slow->next;
        ListNode* phead = NULL;
        
        while (fast)
        {
            ListNode* tmp = fast->next;
            fast->next = phead;
            phead = fast;
            fast = tmp;
        }
        // 将前半段截断
        slow->next = NULL;
        // 后半段节点头结点为phead
        // 将head 与 phead间隔重组
        ListNode* p = head;
        ListNode* q = phead;
        while (phead)
        {
            phead = phead->next;
            q->next = p->next;
            p->next = q;
            q = phead;
            p = p->next->next;
        }
    }

 

725. Split Linked List in Parts

Given a (singly) linked list with head node root, write a function to split the linked list into k consecutive linked list "parts".

The length of each part should be as equal as possible: no two parts should have a size differing by more than 1. This may lead to some parts being null.

The parts should be in order of occurrence in the input list, and parts occurring earlier should always have a size greater than or equal parts occurring later.

Return a List of ListNode's representing the linked list parts that are formed.

Examples 1->2->3->4, k = 5 // 5 equal parts [ [1], [2], [3], [4], null ]

Example 1:

Input: 
root = [1, 2, 3], k = 5
Output: [[1],[2],[3],[],[]]
Explanation:
The input and each element of the output are ListNodes, not arrays.
For example, the input root has root.val = 1, root.next.val = 2, \root.next.next.val = 3, and root.next.next.next = null.
The first element output[0] has output[0].val = 1, output[0].next = null.
The last element output[4] is null, but it's string representation as a ListNode is [].

Example 2:

Input: 
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
Explanation:
The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts.

Note:

The length of root will be in the range [0, 1000].Each value of a node in the input will be an integer in the range [0, 999].k will be an integer in the range [1, 50].

vector<ListNode*> splitListToParts(ListNode* root, int k) {
        vector<ListNode*> res(k);
        int len = 0;
        for (ListNode *t = root; t; t = t->next) ++len;
        int avg = len / k, ext = len % k;
        for (int i = 0; i < k && root; ++i) {
            res[i] = root;
            for (int j = 1; j < avg + (i < ext); ++j) {
                root = root->next;
            }
            ListNode *t = root->next;
            root->next = NULL;//截断
            root = t;         //下一截的头结点
        }
        return res;
    }

 

2. 两数相加

给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        if (!l1) return l2;
        if (!l2) return l1;
        int sum=0, rem=0, carry=0;
        sum = (l1->val + l2->val + carry);
        rem = sum % 10;
        carry = sum / 10;
        ListNode* lhead = new ListNode(rem); //Start digit
        ListNode* lres = lhead;
        l1 = l1->next;
        l2 = l2->next;
        while (l1 != NULL || l2 != NULL) { // Till either number ends
            sum = ((l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry);
            rem = sum % 10;
            carry = sum / 10;
            lres->next = new ListNode(rem);
            lres = lres->next;
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }
        if (carry>0) lres->next = new ListNode(carry); //Left over carry
        return lhead;
        
        //递归解法
        /*
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {  
            return add(l1,l2,0);  
        }  
        ListNode* add(ListNode*l1,ListNode*l2,int carry){  
            if(l1==nullptr&&l2==nullptr&&carry==0)  
                return nullptr;  
            int sum=(l1==nullptr?0:l1->val)+(l2==nullptr?0:l2->val)+carry;  
            ListNode*p=new ListNode(sum%10);  
            p->next=add(l1==nullptr?l1:l1->next,l2==nullptr?l2:l2->next,sum/10);  
            return p;  
        } 
        */
    }

 

445. 两数相加 II

给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:

如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。

示例:

输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        // 将相加的值存入数组,对数组的节点值进行调整,成单个数
        // 再生成链表
        int len1 = 0, len2 = 0;
        for (ListNode* tmp = l1; tmp != NULL; tmp = tmp->next)
            len1++;
        for (ListNode* tmp = l2; tmp != NULL; tmp = tmp->next)
            len2++;
        // 初始化存储vector,长度为最长链表+1,因为最高位可能进位
        int k = len1 > len2 ? len1 : len2;
        vector<int> res(k+1,0);
        int i = 1;
        
        //对齐链表
        ListNode* tmp1 = l1, *tmp2 = l2;
        if (len1 > len2)
        {
            int k = len1-len2;
            while (k--)
            {
                res[i++] = tmp1->val;
                tmp1 = tmp1->next;
            }
        }
        else
        {
            int k = len2-len1;
            while (k--)
            {
                res[i++] = tmp2->val;
                tmp2 = tmp2->next;
            }
        }
        //对齐后相加
        while (tmp1)
        {
            res[i++] = tmp1->val + tmp2->val;
            tmp1 = tmp1->next;
            tmp2 = tmp2->next;
        }
        
        //对值进行调整,调整成个位数
        int c = 0;
        int s = 0;
        for (int j = k; j >= 0; j--)
        {
            s = (res[j] + c)%10;
            c = (res[j] + c)/10;
            res[j] = s;
        }
        
        // 生成链表
        ListNode* head = new ListNode(res[0]);
        ListNode* phead = head;
        for (int j = 1; j <= k; j++)
        {
            phead->next = new ListNode(res[j]);
            phead = phead->next;
        }
        
        if (res[0] == 0)
        {
            ListNode* tmp = head;
            head = head->next;
            delete tmp; //释放多余节点
            return head;
        }
        else 
            return head;
    }

 

817. Linked List Components

 

We are given head, the head node of a linked list containing unique integer values.

We are also given the list G, a subset of the values in the linked list.

Return the number of connected components in G, where two values are connected if they appear consecutively in the linked list.

Example 1:

Input: 
head: 0->1->2->3
G = [0, 1, 3]
Output: 2
Explanation: 
0 and 1 are connected, so [0, 1] and [3] are the two connected components.

Example 2:

Input: 
head: 0->1->2->3->4
G = [0, 3, 1, 4]
Output: 2
Explanation: 
0 and 1 are connected, 3 and 4 are connected, so [0, 1] and [3, 4] are the two connected components.

Note:

  • If N is the length of the linked list given by head1 <= N <= 10000.
  • The value of each node in the linked list will be in the range [0, N - 1].
  • 1 <= G.length <= 10000.
  • G is a subset of all values in the linked list.
int numComponents(ListNode* head, vector<int>& G) {
        // 思路:遍历链表,查找当前节点值是否在G中
        // 查找当前节点是否在数组中出现,如果当前节点的后继节点同样存在,则将指针后移,
        // 如果不存在则res++
        unordered_set<int> setG (G.begin(), G.end());
        int res = 0;
        while (head != NULL) {
            // 链表中下一个元素为空或者下一个元素不在G中,则res+1
            // unordered_set容器可以使用count()直接查找
            if (setG.count(head->val) && (head->next == NULL || !setG.count(head->next->val))) res++;
            head = head->next;
        }
        return res;
    }

 

86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
ListNode* partition(ListNode* head, int x) {
        // 思路:新建两个链表,分别是比x小的和大的
        // 遍历链表,按照节点值大小,分别将节点拼接在各自链表之后
        // 最后将两链表进行拼接即可
        // 另外一种思路:将链表中的值保存在两个数组中(一小一大)
        // 在将数组的值按大小赋值给原链表中的每个节点
        ListNode* smaller = new ListNode(0);
        ListNode* bigger = new ListNode(0);
        ListNode* psmaller = smaller, *pbigger = bigger;
        
        while (head != NULL)
        {
            if (head->val < x)
            {
                psmaller->next = head;
                psmaller = psmaller->next;
            }
            else
            {
                pbigger->next = head;
                pbigger = pbigger->next;
            }
            head = head->next;
        }
        pbigger->next = NULL;
        ListNode* p2 = bigger;
        psmaller->next = bigger->next; //拼接
        delete p2;
        
        ListNode* p1 = smaller;
        smaller = smaller->next;
        delete p1;
        return smaller;
    }

 

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
ListNode* reverseBetween(ListNode* head, int m, int n) {
        ListNode* phead = new ListNode(0);
        phead->next = head;
        // pre 为m节点的前一个   tail为n节点后一个
        ListNode* pre = phead, *tail = phead;
        for (int i = 0; i < m-1; i++)
            pre = pre->next;
        for (int i = 0; i < n+1; i++)
            tail = tail->next;
        
        //将pre->next中(n-m+1)个节点接入到tail之前
        ListNode* tmp = pre->next; // 需要处理的链表
        pre->next = tail;          //被插入的链表
        //处理后,m~n这一段节点头结点为tmp
        //被插入结点头结点为phead; 只需将节点插入到pre节点之后
        
        for (int i = 0; i < n-m+1; i++)
        {
            ListNode* tmp_next = tmp->next;
            tmp->next = pre->next;
            pre->next = tmp;
            tmp = tmp_next;
        }
        //delete phead;
        //return head; 这种释放是错误的,因为head的位置已经改变
        // 应当返回phead->next;
        tmp = phead;
        phead = phead->next;
        delete tmp;
        return phead;
    }

 

82. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

输入: 1->2->3->3->4->4->5
输出: 1->2->5

示例 2:

输入: 1->1->1->2->3
输出: 2->3
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        //定义一个前驱指针和一个现指针,每当前驱指针指向新的节点,
        //现指针从下一个位置开始往下遍历,遇到相同的则继续往下,直到遇到不同项时,
        //把前驱指针的next指向下面那个不同的元素。如果现指针遍历的第一个元素就不相同,
        //则把前驱指针向下移一位。
        if (!head || !head->next) return head;

        ListNode *start = new ListNode(0);
        start->next = head;
        ListNode *pre = start;
        while (pre->next) {
            ListNode *cur = pre->next;
            while (cur->next && cur->next->val == cur->val) 
                cur = cur->next;
            if (cur != pre->next)
            {
                pre->next = cur->next;
                del(pre->next, cur); // 删除节点,防止内存泄漏
            }
            else
            {
                pre = pre->next;
            }
        }
        ListNode* tmp = start;
        start = start->next;
        delete tmp;
        return start;
    }
    
private:
    //删除head到tail节点
    void del(ListNode* head,ListNode* tail){
        while(head != tail->next){
            ListNode* tmp=head;
            head=head->next;
            delete tmp;
        }
    }
};

 

61. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 个位置,其中 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
ListNode* rotateRight(ListNode* head, int k) {
        //思路:其实就是将 链表最后 k%len 个节点移到最开头
        //注意移动后,头结点和尾结点的设置
        if (head == NULL || head->next == NULL)
            return head;
        
        int len = 0;
        ListNode* tail = NULL; // 最终指向原链表尾结点
        for (ListNode* tmp = head; tmp != NULL; tmp = tmp->next)
        {
            tail = tmp;
            len++;
        }
        int offset = k % len;
        //offset为0,直接返回
        if (offset == 0) 
            return head;
        
        ListNode* last = head; //最终指向截断后的尾结点
        ListNode* phead = head;//新链表头结点
        for (int i = 0; i < len-offset-1; i++)
            last = last->next;
        phead = last->next;
        
        last->next = NULL; // 截断
        tail->next = head; // 拼接
        return phead;
    }

 

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

说明:不允许修改给定的链表。

进阶:
你是否可以不用额外空间解决此题?

1、在没找到规律的时候,可以用笨办法解决:

ListNode *detectCycle(ListNode *head) {
        // 思路:快慢指针,有环存在,则快指针一定能追上慢指针
        // 找到快慢指针相遇的节点,快慢指针走的步差即为环的长度
        // 遍历节点,如果节点的len_circle后节点与自己相同,则为环入口
        if (head == NULL || head->next == NULL)
            return NULL;
        
        ListNode* slow = head;
        ListNode* fast = head;
        
        int s = 0;
        int f = 0;
        // 保证fast->next->next存在(一定要保证用到的节点存在)
        while (fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;
            s ++;
            f += 2;
            if (slow == fast)
            {
                //环的长度
                int len_circle = f-s;
                ListNode* p = head;
                while (p != NULL)
                {
                    // 如果某节点后len_circle个节点是自己,则该节点是环的入口
                    ListNode* tmp = p;
                    for (int i = 0; i < len_circle; i++)
                    {
                        tmp = tmp->next;
                    }
                    if (tmp == p)
                    {
                        return tmp;
                    }
                    p = p->next;
                }
            }
        }
        return NULL;
    }

2、快慢指针的步差即为环长度len_circle,而且快指针步长f为慢指针步长s两倍,即:2s-s = len_circle, s = len_circle;

头指针到环入口的距离为m,环入口到快慢指针相遇节点距离为k,相遇节点到环入口距离为x

则 m+k = k + x  => m = x;

ListNode *detectCycle(ListNode *head) {
        ListNode *slow=head,*fast=head;
        while(fast!=NULL && fast->next!=NULL) {
            slow = slow->next;               //Slow moves by 1 step
            fast = fast->next->next;        //Fast moves by two steps
            
            //If they meet then there is a loop
            if(slow==fast) 
            {
                //To find the starting element where the loop starts
                fast = fast;
                slow = head;
                while(slow!=fast) 
                {
                    //Both move by 1 step
                    slow = slow->next;
                    fast=fast->next;
                }
                return slow;
            }
        }
        return NULL; //No loop
    }

 

19. 删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 思路:前后指针pre, back
        // pre指针先走n步,然后pre、back一起走
        // pre到达尾结点的时候,back指针指向倒数第n+1个节点
        // 考虑可能删除头结点,所以在头结点前添加一心的头结点方便操作
        ListNode* phead  = new ListNode(0);
        phead->next = head;
        
        ListNode* pre = phead, *back = phead;
        for (int i = 0; i < n; i++)
        {
            pre = pre->next;
        }
        while (pre->next != NULL)
        {
            pre = pre->next;
            back = back->next;
        }
        
        ListNode* tmp = back->next;
        back->next = tmp->next;
        delete tmp;
        ListNode* del = phead;
        phead = phead->next;
        delete del;
        return phead;
    }

 

138. 复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深度拷贝。 

//首先,题目的意思,就是完全按照给出的链表的关系复制链表,
//比如,原链表中值为1的节点的next指向的是值为2的节点,随机指针指向值为3的节点,
//那么,复制之后,还是应该满足这样的指向关系,但是,因为是复制的链表,
//所以,存储在内存空间中的位置当然和原先不同。

//我们当然第一想法就是一个个复制节点,但是难度在于什么呢?
//复制前面节点的时候并不知道他要指向的节点(不论是随机还是next)在哪个地址。
//所以,直接简单粗暴复制是肯定不行的。
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        //思路一:我们可以考虑用Hash map来缩短查找时间,第一遍遍历生成所有新节点时同时
        //建立一个原节点和新节点的哈希表,第二遍给随机指针赋值时,查找时间是常数级
        /*
        if (head == NULL) return head;
        
        RandomListNode* phead = new RandomListNode(head->label);
        RandomListNode* node = phead;
        RandomListNode* cur = head->next;
        
        map<RandomListNode*, RandomListNode*> m;
        m[head] = phead;
        while (cur)
        {
            node->next = new RandomListNode(cur->label);
            m[cur] = node->next; // 建立 原节点 与 复制节点 对应关系
            
            node = node->next;
            cur = cur->next;
        }
        node = phead;
        cur = head;
        while (node)
        {
            // cur->random在哈希表中对应的节点正式node->random指向的节点
            node->random = m[cur->random];
            node = node->next;
            cur = cur->next;
        }
        return phead;*/
        //思路二:
        //1. 在原链表的每个节点后面拷贝出一个新的节点
        //2. 依次给新的节点的随机指针赋值,而且这个赋值非常容易 cur->next->random = cur->random->next
        //3. 断开链表可得到深度拷贝后的新链表
        if (!head) return NULL;
        RandomListNode *cur = head;
        while (cur) {
            RandomListNode *node = new RandomListNode(cur->label);
            node->next = cur->next;
            cur->next = node;
            cur = node->next;
        }
        cur = head;
        while (cur) {
            if (cur->random) {
                cur->next->random = cur->random->next;
            }
            cur = cur->next->next;
        }
        cur = head;
        RandomListNode *res = head->next;
        while (cur) {
            RandomListNode *tmp = cur->next;
            cur->next = tmp->next;
            if(tmp->next) tmp->next = tmp->next->next;
            cur = cur->next;
        }
        return res;
    }
};

 

25. k个一组翻转链表

给出一个链表,每 个节点一组进行翻转,并返回翻转后的链表。

是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 的整数倍,那么将最后剩余节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 = 2 时,应当返回: 2->1->4->3->5

当 = 3 时,应当返回: 3->2->1->4->5

说明 :

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
方法一:想到之前的翻转链表的第m~n的节点
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if (head == NULL) return head;
        if (k == 0 || k == 1) return head;
        int len = 0;
        for (ListNode* tmp = head; tmp != NULL; tmp = tmp->next)
        {    
            len++;
        }
        //思路:
        //将链表分成g组,对每组进行翻转
        //调用之前写的,对一个链表第m~n节点进行翻转
        int g = len/k;
        for (int i = 1; i <= g; i++)
        {
           head = reverseBetween(head, i*k-(k-1), i*k);
        }
        return head;
    }
private:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        ListNode* phead = new ListNode(0);
        phead->next = head;
        // pre 为m节点的前一个   tail为n节点后一个
        ListNode* pre = phead, *tail = phead;
        for (int i = 0; i < m-1; i++)
            pre = pre->next;
        for (int i = 0; i < n+1; i++)
            tail = tail->next;
        
        //将pre->next中(n-m+1)个节点接入到tail之前
        ListNode* tmp = pre->next; // 需要处理的链表
        pre->next = tail;          //被插入的链表
        //处理后,m~n这一段节点头结点为tmp
        //被插入结点头结点为phead; 只需将节点插入到pre节点之后
        
        for (int i = 0; i < n-m+1; i++)
        {
            ListNode* tmp_next = tmp->next;
            tmp->next = pre->next;
            pre->next = tmp;
            tmp = tmp_next;
        }
        //delete phead;
        //return head; 这种释放是错误的,因为head的位置已经改变
        // 应当返回phead->next;
        tmp = phead;
        phead = phead->next;
        delete tmp;
        return phead;
    }
};
方法二:遍历一遍,每k个节点翻转一次
递归,解决
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(k==1) return head;
        ListNode* p = head;
        ListNode* q = head;
        for(int i=k;i>1;i--){     //find the last node in the k nodes
            if(!q||!q->next) return head;
            q = q->next;
        }
        ListNode* r = p;
        for(int i=k;i>1;i--){    //insert p into q->next until the k nodes reserved
            ListNode* t = p->next;
            p->next = q->next;
            q->next = p;
            p = t;
        }
        head = q;
        r->next = reverseKGroup(r->next,k);
        return head;
    }
};

 

23. 合并K个排序链表

合并 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int k = lists.size();
        if (k == 0) return NULL;
        if (k == 1) return lists[0];
        return __mergeKLists(lists, k-1);
    }
    ListNode* __mergeKLists(vector<ListNode*>& lists, int r)
    {
        while (r > 0)
        {
            int l = 0;
            while (l < r)
            {
                lists[l] = mergeTwoLists(lists[l], lists[r]);
                l++;
                r--;
            }
        }
        return lists[0];
        
    }
    
// 合并两个排序链表
private:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // 比较节点值大小 并 归并的过程
        if (l1 == NULL)
            return l2;
        if (l2 == NULL)
            return l1;
        ListNode* head = NULL;
        //ListNode* head = new ListNode(0);
        if (l1->val < l2->val)
        {
            head = l1;
            l1 = l1->next;
        }
        else
        {
            head = l2;
            l2 = l2->next;
        }
        
        ListNode* phead = head;
        while (l1 != NULL && l2 != NULL)
        {
            if (l1->val < l2->val)
            {
                phead->next = l1;
                l1 = l1->next;
            }

            else
            {
                phead->next = l2;
                l2 = l2->next;
            }
            phead = phead->next;
        }
        
        //l1先遍历完,将l2接入新链表中
        if (l1 == NULL)
            phead->next = l2;
        
        //l2先遍历完,将l1接入新链表中
        if (l2 == NULL)
            phead->next = l1;
        
        return head;
    }
};

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值