算法经典面试题(3)——链表

*题目编号为Leetcode中对应的题号。
某位大佬的Leetcode题解参考链接

链表

  • 给一个节点需要注意:
    • 如果是头节点、尾节点或者空节点等特殊情况该如何处理
  1. (206反转链表) 反转一个单链表。

    示例:

    输入: 1->2->3->4->5->NULL
    输出: 5->4->3->2->1->NULL
    
/**
     * 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 == NULL)
    			return head;
    		ListNode* curr = head;
    		ListNode* next = head->next;
    		ListNode* pre = NULL;
    		while (true) {
    
    
    			curr->next = pre;
    			if (next == NULL)// 如果next为空则不移动
    				break;
    			pre = curr;
    			curr = next;
    			next = next->next;
    		}
    		return curr;
        }
    };
  1. (92反转链表II) 反转从位置 mn 的链表。请使用一趟扫描完成反转。

    说明: 1 ≤ mn ≤ 链表长度。

    示例:

    输入: 1->2->3->4->5->NULL, m = 2, n = 4
    输出: 1->4->3->2->5->NULL
    
class Solution {
    
    
    public:
        ListNode* reverseBetween(ListNode* head, int m, int n) {
    
    
    
            ListNode* dummyHead = new ListNode(-1);
            dummyHead->next = head;
    
            ListNode* pre = dummyHead;
            for(int i = 0; i < m - 1; i ++, pre = pre->next);
    
            ListNode* tail = pre->next;
            ListNode* left;
            pre->next = reverse(pre->next, n - m, left);
            tail->next = left;
    
            ListNode* ret = dummyHead->next;
            delete dummyHead;
            return ret;
        }
    
    private:
        ListNode* reverse(ListNode* head, int index, ListNode* &left){
    
    
    
            if(index == 0){
    
    
                left = head->next;
                return head;
            }
    
            ListNode* tail = head->next;
            ListNode* ret = reverse(head->next, index - 1, left);
            tail->next = head;
            return ret;
        }
    };
  1. (83删除排序链表中的重复元素) 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

    示例 :

    输入: 1->1->2
    输出: 1->2
    
    class Solution {
    
    
    public:
        ListNode* deleteDuplicates(ListNode* head) {
    
    
    
            ListNode* cur = head;
            while(cur && cur->next){
    
    
                if(cur->val == cur->next->val) cur->next = cur->next->next;
                else cur = cur->next;
            }
            return head;
        }
    };
  1. (86分隔链表) 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。

    示例:

    输入: head = 1->4->3->2->5->2, x = 3
    输出: 1->2->2->4->3->5
    
/// Linear Scan
    /// Time Complexity: O(n)
    /// Space Complexity: O(1)
    class Solution {
    
    
    public:
        ListNode* partition(ListNode* head, int x) {
    
    
    
            ListNode* dummyHead1 = new ListNode(-1);
            ListNode* dummyHead2 = new ListNode(-1);
            ListNode* prev1 = dummyHead1;
            ListNode* prev2 = dummyHead2;
    
            for(ListNode* cur = head ; cur != NULL ;){
    
    
                if(cur->val < x){
    
    
                    prev1->next = cur;
                    cur = cur->next;
                    prev1 = prev1->next;
                    prev1->next = NULL;
                }
                else{
    
    
                    prev2->next = cur;
                    cur = cur->next;
                    prev2 = prev2->next;
                    prev2->next = NULL;
                }
            }
    
            prev1->next = dummyHead2->next;
            ListNode* ret = dummyHead1->next;
    
            delete dummyHead1;
            delete dummyHead2;
            return ret;
        }
    };
  1. (328奇偶链表) 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

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

    示例:

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

    说明:

    • 应当保持奇数节点和偶数节点的相对顺序。
    • 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
/// Split the Linked List into two and then merge
    /// Time Complexity: O(n)
    /// Space Complexity: O(1)
    class Solution {
    
    
    public:
        ListNode* oddEvenList(ListNode* head) {
    
    
    
            if(head == NULL || head->next == NULL || head->next->next == NULL)
                return head;
    
            ListNode* dummyHead1 = new ListNode(-1);
            ListNode* dummyHead2 = new ListNode(-1);
            ListNode* p1 = dummyHead1;
            ListNode* p2 = dummyHead2;
            ListNode* p = head;
            for(int i = 0; p; i ++)
                if(i % 2 == 0){
    
    
                    p1->next = p;
                    p = p->next;
                    p1 = p1->next;
                    p1->next = NULL;
                }
                else{
    
    
                    p2->next = p;
                    p = p->next;
                    p2 = p2->next;
                    p2->next = NULL;
                }
    
            p1->next = dummyHead2->next;
            ListNode* ret = dummyHead1->next;
    
            delete dummyHead1;
            delete dummyHead2;
            return ret;
        }
    };
  1. (2两数相加) 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

    示例:

    输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
    输出:7 -> 0 -> 8
    原因:342 + 465 = 807
    
/// Create new LinkedList for result
    /// Time Complexity: O(n)
    /// Space Complexity: O(n)
    class Solution {
    
    
    public:
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    
    
    
            ListNode *p1 = l1, *p2 = l2;
            ListNode *dummyHead = new ListNode(-1);
            ListNode* cur = dummyHead;
            int carried = 0;
            while(p1 || p2 ){
    
    
                int a = p1 ? p1->val : 0;
                int b = p2 ? p2->val : 0;
                cur->next = new ListNode((a + b + carried) % 10);
                carried = (a + b + carried) / 10;
    
                cur = cur->next;
                p1 = p1 ? p1->next : NULL;
                p2 = p2 ? p2->next : NULL;
            }
    
            cur->next = carried ? new ListNode(1) : NULL;
            ListNode* ret = dummyHead->next;
            delete dummyHead;
            return ret;
        }
    };
  1. (445两数相加II) 给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

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

    示例:

    输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
    输出:7 -> 8 -> 0 -> 7
    
/// Using Stack
    /// Time Complexity: O(m + n + max(m, n))
    /// Space Complexity: O(m + n)
    class Solution {
    
    
    public:
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    
    
    
            stack<ListNode*> stack1, stack2, stack;
            ListNode* node = l1;
            while(node) stack1.push(node), node = node->next;
            node = l2;
            while(node) stack2.push(node), node = node->next;
    
            int carry = 0;
            while(!stack1.empty() || !stack2.empty() || carry){
    
    
    
                int x = 0;
                if(!stack1.empty()) x += stack1.top()->val, stack1.pop();
                if(!stack2.empty()) x += stack2.top()->val, stack2.pop();
                x += carry;
    
                stack.push(new ListNode(x % 10));
                carry = x / 10;
            }
    
            ListNode* ret = stack.top(), *cur = ret;
            stack.pop();
            while(!stack.empty())
                cur->next = stack.top(), cur = cur->next, stack.pop();
            return ret;
        }
    };
  1. (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 NULL;
    
    	while (head != NULL && head->val == val) {
    
    
    		ListNode* delNode = head;
    		head = delNode->next;
    		delete delNode;
    	}
    
    	if (head == NULL)
    		return NULL;
    
    	ListNode* curr = head;
    	while (curr->next != NULL) {
    
    
    		if (curr->next->val == val) {
    
    // 删除下一个节点
    			ListNode* delNode = curr->next;
    			curr->next = delNode->next;
    			delete delNode;
    		}
    		else
    			curr = curr->next;
    	}
    	return head;
    }
    
    // 设立虚拟头结点
    ListNode* removeElements(ListNode* head, int val) {
    
    
        ListNode* dummyHead=new ListNode(0);
        dummyHead->next=head;
        ListNode* curr=dummyHead;
        while(curr->next != NULL){
    
    
            if(curr->next->val == val){
    
    
                ListNode* delNode=curr->next;
                curr->next=delNode->next;
                delete delNode;
            }
            else
                curr=curr->next;
        }
        ListNode* ret=dummyHead->next;
        delete dummyHead;
        return ret;
    }
  1. (82删除排序链表中的重复元素II) 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

    示例:

    输入: 1->2->3->3->4->4->5
    输出: 1->2->5
    输入: 1->1->1->2->3
    输出: 2->3
    
class Solution {
    
    
    public:
        ListNode* deleteDuplicates(ListNode* head) {
    
    
    
            ListNode* dummyHead = new ListNode(-1);
            dummyHead->next = head;
    
            ListNode* prev = dummyHead;
            ListNode* cur = prev->next;
            while(cur){
    
    
    
                int num = 0;
                ListNode* p = cur;
                while(p && p->val == cur->val){
    
    
                    num ++;
                    p = p->next;
                }
    
                if(num > 1)
                    prev->next = p;
                else
                    prev = cur;
                cur = p;
            }
    
            ListNode* ret = dummyHead->next;
            delete dummyHead;
            return ret;
        }
    };
  1. (21合并两个有序链表) 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

    示例:

    输入:1->2->4, 1->3->4
    输出:1->1->2->3->4->4
    
/// Iterative
     /// Time Complexity: O(len(l1) + len(l2))
     /// Space Complexity: O(1)
     class Solution {
    
    
     public:
         ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    
    
     
             ListNode* dummyHead = new ListNode(-1);
             ListNode* p = dummyHead;
             ListNode* l1p = l1;
             ListNode* l2p = l2;
             while(l1p != NULL && l2p != NULL){
    
    
                 if(l1p->val < l2p->val){
    
    
                     p->next = l1p;
                     l1p = l1p->next;
                 }
                 else{
    
    
                     p->next = l2p;
                     l2p = l2p->next;
                 }
     
                 p = p->next;
             }
     
             if(l1p != NULL)
                 p->next = l1p;
             else
                 p->next = l2p;
     
             ListNode* ret = dummyHead->next;
             dummyHead->next = NULL;
             delete dummyHead;
     
             return ret;
         }
     };
  1. (24两两交换链表中的节点) 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

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

    示例:

    给定 1->2->3->4, 你应该返回 2->1->4->3.
    
ListNode* swapPairs(ListNode* head) {
    
    
     	ListNode* dummyHead = new ListNode(0);
     	dummyHead->next = head;
     	ListNode* pre = dummyHead;
     
     	// 交换pair1和pair2两个节点
     	while (pre->next && pre->next->next) {
    
    
     		ListNode* pair1 = pre->next;
     		ListNode* pair2 = pair1->next;
     		ListNode* end = pair2->next;
     		// 交换
     		pre->next = pair2;
     		pair2->next = pair1;
     		pair1->next = end;
     		// 前移
     		pre = pair1;
     	}
     	ListNode* retNode = dummyHead->next;
     	delete dummyHead;
     	return retNode;
     }
  1. (25K个一组翻转链表) 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

    示例:

    给你这个链表:1->2->3->4->5
    当 k = 2 时,应当返回: 2->1->4->3->5
    当 k = 3 时,应当返回: 3->2->1->4->5
    
class Solution {
    
    
     public:
         ListNode* reverseKGroup(ListNode* head, int k) {
    
    
     
             ListNode* dummyHead = new ListNode(-1);
             dummyHead->next = head;
     
             ListNode* pre = dummyHead;
             while(pre && pre->next){
    
    
     
                 ListNode* end = pre;
                 int i;
                 for(i = 0; i < k && end->next; i ++)
                     end = end->next;
     
                 if(i != k) break;
     
                 ListNode* next = end->next;
     
                 // reverse from pre->next to end
                 ListNode* rhead = reverse(pre->next, end);
     
                 ListNode* tail = pre->next;
                 pre->next = rhead;
                 tail->next = next;
                 pre = tail;
             }
     
             ListNode* ret = dummyHead->next;
             delete dummyHead;
             return ret;
         }
     
     private:
         ListNode* reverse(ListNode* head, ListNode* end){
    
    
     
             if(head == end) return head;
     
             ListNode* rhead = reverse(head->next, end);
             head->next->next = head;
             return rhead;
         }
     };
  1. (148排序链表)O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

    示例:

    输入: 4->2->1->3
    输出: 1->2->3->4
    输入: -1->5->3->4->0
    输出: -1->0->3->4->5
    
/// Merge Sort Top Down
     /// Time Complexity: O(nlogn)
     /// Space Complexity: O(logn)
     class Solution {
    
    
     public:
         ListNode* sortList(ListNode* head) {
    
    
     
             if(head == NULL || head->next == NULL)
                 return head;
     
             ListNode* slow = head;
             ListNode* fast = head->next;
             while(fast && fast->next){
    
    
                 slow = slow->next;
                 fast = fast->next->next;
             }
     
             ListNode* head2 = slow->next;
             slow->next = NULL;
             head = sortList(head);
             head2 = sortList(head2);
             return merge(head, head2);
         }
     
     private:
         ListNode* merge(ListNode* a, ListNode* b){
    
    
     
             ListNode* dummyHead = new ListNode(-1);
             ListNode *p1 = a, *p2 = b, *p = dummyHead;
             while(p1 && p2)
                 if(p1->val < p2->val){
    
    
                     p->next = p1;
                     p1 = p1->next;
                     p = p->next;
                     p->next = NULL;
                 }
                 else{
    
    
                     p->next = p2;
                     p2 = p2->next;
                     p = p->next;
                     p->next = NULL;
                 }
             if(p1) p->next = p1;
             if(p2) p->next = p2;
     
             ListNode* ret = dummyHead->next;
             delete dummyHead;
             return ret;
         }
     };
  1. (237删除链表中的节点) 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

    示例 :

    输入: head = [4,5,1,9], node = 5
    输出: [4,1,9]
    解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
    
// 将所有节点赋值为下一个节点的值
     void deleteNode(ListNode* node) {
    
    
         if(node==NULL)
             return;
         ListNode* next=node->next;
         ListNode* nnext=next->next;
         while(nnext!=NULL){
    
    
             node->val=next->val;
             node=next;
             next=node->next;
             nnext=next->next;
         }
         node->val=next->val;
         node->next=NULL;
     }
     
     // 将当前节点赋值为下一节点,然后删除下一节点
     void deleteNode(ListNode* node) {
    
    
         if(node==NULL)// 删除空节点
             return;
         if(node->next=NULL){
    
    // 删除尾节点
     		delete node;
             node=NULL;
             return;
         }
         ListNode* delNode=node->next;
         node->val=delNode->val;
         node->next=delNode->next;
         delete delNode;
     }
  1. (19删除链表的倒数第n个节点) 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

    示例:

    给定一个链表: 1->2->3->4->5, 和 n = 2.
    
    当删除了倒数第二个节点后,链表变为 1->2->3->5.
    
// 先遍历一遍求链表长度,然后删除第size-n个节点
     ListNode* removeNthFromEnd(ListNode* head, int n) {
    
    
         // 计算链表的长度
         int size=0;
         ListNode* curr=head;
         while(curr!=NULL){
    
    
     		curr=curr->next;
             size++;
         }
         // 删除第size-n个节点
         int count=0;
         ListNode* dummyHead=new ListNode(0);// 设立虚拟头结点
         curr=dummyHead;
         while(count<size-n){
    
    // 要删的是curr->next节点
             curr=curr->next;
             count++;
         }
         ListNode* delNode=curr->next;
         curr->next=delNode->next;
         delete delNode;
     	ListNode* retNode = dummyHead->next;
     	delete dummyHead;
     	return retNode;
     }
     
     // 遍历一遍链表
     ListNode* removeNthFromEnd(ListNode* head, int n) {
    
    
         ListNode* dummyHead=new ListNode(0);
         dummyHead->next=head;
         ListNode* l=dummyHead;
         ListNode* r=dummyHead;
         // 拉开l与r的距离
         for(int i=0;i<n;i++)
             r=r->next;
         // 将r移到链表尾,l保持与r的距离
         while(r->next!=NULL){
    
    
     		r=r->next;
             l=l->next;
         }
         // 删除l->next节点
         ListNode* delNode=l->next;
         l->next=delNode->next;
         delete delNode;
         // 返回头节点
         ListNode* ret=dummyHead->next;
         delete dummyHead;
         return ret;
     }
  1. (61旋转链表) 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

    示例:

    输入: 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
    
/// Brute Force
     /// Two Pointers
     ///
     /// Time Complexity: O(N)
     /// Space Complexity: O(1)
     class Solution {
    
    
     public:
         ListNode* rotateRight(ListNode* head, int k) {
    
    
     
             if(head == NULL)
                 return NULL;
     
             int len = get_len(head);
             k = k % len;
     
             ListNode* end = head;
             for(int i = 0 ; i < k ; i ++)
                 end = end->next;
     
             ListNode* start = head;
             while(end->next != NULL){
    
    
                 start = start->next;
                 end = end->next;
             }
     
             end->next = head;
             head = start->next;
             start->next = NULL;
     
             return head;
         }
     
     private:
         int get_len(ListNode* head){
    
    
             int res = 0;
             while(head){
    
    
                 res ++;
                 head = head->next;
             }
             return res;
         }
     };
  1. (143重排链表) 给定一个单链表 LL0→L1→…→L(n-1)→Ln ,将其重新排列后变为: L0→Ln→L1→Ln-1→L2→L(n-2)→…

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

    示例:

    给定链表 1->2->3->4, 重新排列为 1->4->2->3.
    给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
    
class Solution {
    
    
     public:
         void reorderList(ListNode* head) {
    
    
     
             if(!head || !head->next) return;
     
             ListNode* slow = head, *fast = head;
             while(fast->next && fast->next->next)
                 slow = slow->next, fast = fast->next, fast = fast->next;
     
             ListNode* head1 = head, *head2 = slow->next;
             slow->next = NULL;
             head2 = reverse(head2);
     
             ListNode* dummyHead = new ListNode(-1);
             ListNode* cur= dummyHead, *cur1 = head1, *cur2 = head2;
             for(int i = 0; cur1 || cur2; i ++)
                 if(i % 2 == 0) cur->next = cur1, cur = cur->next, cur1 = cur1->next;
                 else cur->next = cur2, cur = cur->next, cur2 = cur2->next;
             head = dummyHead->next;
         }
     
     private:
         ListNode* reverse(ListNode* node){
    
    
     
             if(!node->next) return node;
             ListNode* ret = reverse(node->next);
             node->next->next = node;
             node->next = NULL;
             return ret;
         }
     };
  1. (234回文链表) 请判断一个链表是否为回文链表。

    示例:

    输入: 1->2
    输出: false
    输入: 1->2->2->1
    输出: true
    
/// Two Pointers to Reverse and Traverse
     /// Time Complexity: O(n)
     /// Space Complexity: O(1)
     class Solution {
    
    
     public:
         bool isPalindrome(ListNode* head) {
    
    
     
             if(head == NULL || head->next == NULL)
                 return true;
     
             ListNode* slow = head;
             ListNode* fast = head;
             while(fast->next != NULL && fast->next->next != NULL){
    
    
                 slow = slow->next;
                 fast = fast->next->next;
             }
     
             slow->next = reverse(slow->next);
     
             slow = slow->next;
             ListNode* cur = head;
             while(slow != NULL){
    
    
                 if(cur->val != slow->val)
                     return false;
                 else{
    
    
                     slow = slow->next;
                     cur = cur->next;
                 }
             }
             return true;
         }
     
     private:
         ListNode* reverse(ListNode* head){
    
    
     
             if(head == NULL || head->next == NULL)
                 return head;
     
             ListNode* pre = head;
             ListNode* cur = head->next;
             ListNode* next = cur->next;
             head->next = NULL;
     
             while(true){
    
    
                 cur->next = pre;
                 pre = cur;
                 cur = next;
                 if(cur == NULL)
                     break;
                 next = cur->next;
             }
     
             return pre;
         }
     };

猜你喜欢

转载自blog.csdn.net/qq_34731182/article/details/113630231
今日推荐