链表题目总结

单链表

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.

Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function.
分析:
这句话值得注意 Write a function to delete a node (except the tail) 因为尾节点我们不能利用这个函数删掉,这里需要注意~
下面这个版本可以过,但是思路是的。

class Solution {
public:
    void deleteNode(ListNode* node) {
        if(node == NULL) return;
        if(node->next == NULL) {node = NULL;return;} // no use
        node->val = node->next->val;
        node->next = node->next->next;
        return;
    }
};

正确的版本:

class Solution {
public:
    void deleteNode(ListNode* node) {
        if(node == NULL || node->next == NULL) return;
        node->val = node->next->val;
        node->next = node->next->next;
        return;
    }
};

2016.5.1补充
有人问为什么不可以直接用node=node.next赋值
还是对指针和参数的概念不清楚的问题。node是个指针类型的变量,它的值是一个地址,在这个方法中是个传进来的指针参数。你修改了参数的值,不能影响实际链表该节点的值。举个熟悉的例子。

void change(int x){
    x = 4;
}
int man(){
int x = 2;
change(x);
cout << x << endl;  // x = 2
}

所以这题只能修改node的val和next,修改他俩是通过实际节点的地址来修改的,所以实际的节点也会改变。

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
自己考虑几个样例:

  • {[], 1}
  • {[1], 1}
  • {[1,1], 1}
  • {[2, 1], 1}
  • {[2,1,1], 1}

recursion

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        if(head == NULL) return head;
        if(head->val == val) return removeElements(head->next, val);
        head->next = removeElements(head->next, val);
        return head;
    }
};

思路:此题考察了对指针的理解,非常容易出错。如果需要删除头、尾的节点需要特别留意。最好还是在head之前构造一个节点方便统一,用p->next操作下一个节点。因为如果直接要删p 节点,p = NULL这种操作使没有作用的。
iteration

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode fakeHead(-1), *p = &fakeHead;
        fakeHead.next = head;
        while(p->next){
            if(p->next->val == val)
                p->next = p->next->next;
            else 
                p = p->next;
        } return fakeHead.next;
    }
};

单链表反转/逆序

struct Node{
int val;
Node* next;
};
请实现一个反转单链表的函数, 函数原型为:
Node* reverseList(Node* head);
给定参数是单链表的头指针,请返回翻转后的单链表的头指针。空间复杂度要求为O(1)

思路:①->②->③->*->NULL
          p1   p2   p3
首先考虑0个节点和1个节点的链表,此时不需反转。
其余情况我们从左至右,一条一条反转链接的方向,代码:

#include <iostream>
using namespace std;

struct Node{
    int val;
    Node* next;
};
Node* makeList(int n){
    if(!n) {return NULL;}
    Node* head = new Node, *p = head;
    head->next = NULL;
    head->val = 0;

    for(int i = 1; i < n; i++){
        Node* newNode = new Node;
        newNode->next = NULL;
        newNode->val = i;
        p->next = newNode;
        p = p->next;
    } 
    return head; 
}
Node* reverseList(Node* head){
    if(head == NULL || head->next == NULL){return head;}
    Node *p1 = head, *p2 = head->next, *p3;
    while(p2 != NULL){
        p3 = p2->next;
        p2->next = p1;
        p1 = p2;
        p2 = p3;
    } head->next = NULL;
    return p1;
}
void printList(Node* head){
    while(head != NULL){
        cout << head->val << " -> ";
        head = head->next;
    } cout << "NULL\n";
}
const int SIZE = int(9);// test 9 or 10
int main(){
    Node* head = makeList(SIZE);
    printList(head);
    cout << "\n"; 
    head = reverseList(head);
    printList(head);
    return 0;
} 

递归的方法:

Node* helper(Node* p1, Node* p2){
    if(p2 == NULL){ 
        return p1;
    }
    Node* p3 = p2->next;
    p2->next = p1;
    return helper(p2, p3);
}
Node* reverseList(Node*& head){
    if(head == NULL || head->next == NULL){return head;}
    Node* p1 = head->next;
    head->next = NULL;
    return helper(head, p1);
}

查找单链表的中点

输入一个单链表的头结点,要求遍历一次找到中点。如果节点的个数是偶数,输出中点靠左的节点。
思路:如果可以遍历多次可以先遍历一次得到链表的节点数n,再遍历n/2找到中间节点。但题目要求只遍历一次,所以可以用快慢指针的方法。慢指针一次走一个节点,快指针一次走两个节点。验证代码的时候要分含有0(没有节点),1(奇数),2(偶数)个节点的情况。代码:

Node* findMidNode(Node* head){
    if(head == NULL){return head;}
    Node *slow = head, *fast = head->next;
    while(fast != NULL){
        fast = fast->next;
        if(fast != NULL){
            fast = fast->next;
            slow = slow->next;
        }
    } return slow;
} 

如果修改要求,当单链表有偶数个节点时输出靠右的中间节点,需要修改fast的起点:

Node *fast = head; 

判断单链表是否有环

LeetCode 链接:Linked List Cycle
快慢指针法,如果快指针能追上慢指针证明有环:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == NULL) {
            return false;
        }
        ListNode *slow = head, *fast = head->next;
        while(fast != NULL){
            fast = fast->next;
            if(fast == slow){return true;}
            if(fast != NULL){
                fast = fast->next;
                slow = slow->next;
            }
        } return false;
    }
};

哈希表:
We go through each node one by one and record each node’s reference (or memory address) in a hash table. If the current node is null, we have reached the end of the list and it must not be cyclic. If current node’s reference is in the hash table, then return true.
Complexity analysis:

Time complexity : O(n). We visit each of the n elements in the list at most once. Adding a node to the hash table costs only O(1) time.

Space complexity: O(n). The space depends on the number of elements added to the hash table, which contains at most n elements.

输出单链表环的起始节点

LeetCode 链接:Linked List Cycle II
思路:
含有环的情况如图
假设不在环上的部分有X个节点,环上有L个节点。利用快、慢指针法,慢指针每次1步,快指针每次2步。假设快慢指针第一次相遇在环上的第K个节点上,那么由快慢指针的速度关系可以推出:
X+L+K = 2(X+K) 化简一下可知:L = X+K
又因为此时慢指针在环上已经走了K步,环的长度为L,则还需要走 L-K=X步就能回到环上的终点,走X+1步能回到环上的起点,即所求节点。
所以在第一次相遇之后,把快指针调回链表的head,慢指针走到环上的第(K+1)个节点,然后两个指针每次走一步,X步之后两者会相遇到环上的起点。

代码:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == NULL) {
            return NULL;
        }
        bool existLoop = false;
        ListNode *slow = head, *fast = head->next;
        while(fast != NULL){
            fast = fast->next;
            if(fast != NULL){
                fast = fast->next;
                slow = slow->next;
                if(fast == slow){
                    existLoop = true;
                    break;
                }
            }
        }
        if(existLoop){
            fast = head;
            slow = slow->next;
            while(fast != slow){
                fast = fast->next;
                slow = slow->next;
            } return fast;
        }
        return NULL;
    }
};

Odd Even Linked List

Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes.

You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.

Example:
Given 1->2->3->4->5->NULL,
return 1->3->5->2->4->NULL.

Note:
The relative order inside both the even and odd groups should remain as it was in the input.
The first node is considered odd, the second node even and so on …


class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if(!head){return head;}
        ListNode* odd = head, *even = head->next, *evenHead = even;
        while(even && even->next){
            odd->next = even->next;
            odd = odd->next;
            even->next = odd->next;
            even = even->next;
        } odd->next = evenHead;
        return head;
    }
};

总结:好久不写代码了,这题我居然卡了一小时,TLE。原因在于我想省一个变量evenHead。第12行代码:odd->next = head->head; 但这时链表结构早被修改了,切记切记。

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->NULL, m = 2 and n = 4,

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

Note:
Given m, n satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.
思路:链表划分为A->B->C,B段用于反转。做一个fakeNode放在head节点之前,这样可以统一很多情况而且便于return(没有A段,或者没有A&C段)

/**
 * 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) { // A->B->C
        ListNode fakeNode(-1), *subListAEnd = &fakeNode, *subListBHead;
        fakeNode.next = head;
        for(int i = 1; i < m; i++){
            subListAEnd = subListAEnd->next;
        } subListBHead = subListAEnd->next;
        ListNode *p1 = subListBHead, *p2 = p1->next, *p3;
        if(p2){ // at least two nodes in B
            for(int i = 0; i < n-m; i++){
                p3 = p2->next;
                p2->next = p1;
                p1 = p2;
                p2 = p3;
            }
            subListBHead->next = p2;
        } subListAEnd->next = p1;
        return fakeNode.next;
    }
};

Palindrome Linked List

Given a singly linked list, determine if it is a palindrome.

Follow up:
Could you do it in O(n) time and O(1) space?
思路:
可以翻转前一半或者后一半的链表,然后和另一半做比较即可。
我的这种方法是1.5pass的(边找中点边反转),应该是最省的了,但是想的时候很容易出错。建议还是先找到中间节点,再翻转。判断回文需要考虑奇、偶数。
注:题目检测时会检查链表(样例有bug),你修改过的链表不能有环,不然会TLE

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(!head || head->next == NULL) return true; // at least two nodes
        if(head->next->next == NULL){
            if(head->val == head->next->val) return true;
            return false;
        }  // at least three node
        ListNode *slow = head, *fast = head->next, *p2 = slow->next, *p3; // slow->p2->p3
        bool isOddLength = true;
        while(fast){
            fast = fast->next;
            if(fast){
                p3 = p2->next;
                p2->next = slow;
                slow = p2;
                p2 = p3;

                fast = fast->next; 
            } else{
                isOddLength = false;
            }
        }
        if(isOddLength) slow = slow->next;                   
        head->next = NULL; // remove the loop, otherwise the test will be TLE
        while(p3){
            if(slow->val != p3->val) return false;
            slow = slow->next;
            p3 = p3->next;
        }
        return true;
    }
};

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.

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head == NULL || head->next == NULL) return head;
        ListNode fakeHead(-1);
        fakeHead.next = head;
        ListNode* p1 = &fakeHead, *p2 = head, *p3;  // p1->swap(p2->p3)
        while(p2 && p2->next){ // even and odd
            p3 = p2->next;
            p1->next = p3;
            p1 = p2;
            ListNode* tmp = p3->next;
            p3->next = p2;
            p2 = tmp;
        } p1->next = p2;
        return fakeHead.next;
    }
};

Intersection of Two Linked Lists

Write a program to find the node at which the intersection of two singly linked lists begins.

For example, the following two linked lists:

A:         a1 → a2
                         ↘
                            c1 → c2 → c3
                           ↗
B: b1 → b2 → b3
begin to intersect at node c1.

Notes:

  • If the two linked lists have no intersection at all, return null.
  • The linked lists must retain their original structure after the function returns.
  • You may assume there are no cycles anywhere in the entire linked structure.
  • Your code should preferably run in O(n) time and use only O(1) memory.
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA = getLen(headA);
        int lenB = getLen(headB);
        if(lenA < lenB){
            swap(lenA, lenB);
            swap(headA, headB);
        }
        while(lenA > lenB){
            headA = headA->next;
            lenA--;
        }
        while(headA != headB){
            headA = headA->next;
            headB = headB->next;
        } return headA;
    }
    private:
    int getLen(ListNode *head){
        int len = 0;
        while(head){  // head != NULL
            ++len;
            head = head->next;
        } return len;
    }
};

Leetcode中给出了几个解法,我上面的代码不在前三种方法中。
对于Approach 3: Two Pointers,我感觉是这样的:(1)有交点的情况。长度相同,第一次遍历就能相遇在交点;长度不同时,在第二次遍历这两个链表时就能相遇在交点(2)没有交点时。长度相同,第一次遍历时两个指针都走到NULL返回了;但是如果长度不相同,可能会陷入死循环。例子:链表A长度为3,链表B长度为1.

Reorder List

Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

You must do this in-place without altering the nodes’ values.

For example,
Given {1,2,3,4}, reorder it to {1,4,2,3}.

思路:找中点+反转+merge

class Solution {
public:
    void reorderList(ListNode* head) {
        if(head == NULL || head->next == NULL) return; // at least two nodes
        ListNode* slow = head, *fast = head->next; // find middle,len(list1) >= len(lest2)
        while(fast){
            fast = fast->next;
            if(fast){
                fast = fast->next;
                slow = slow->next;
            }
        }
        ListNode * subHead = slow->next;    
        slow->next = NULL;
        ListNode* p1 = subHead; // reverse sublist
        if(subHead->next){  // at least 2 nodes in sublist
            ListNode *p2 = subHead->next, *p3;
            subHead->next = NULL;
            while(p2){
                p3 = p2->next;
                p2->next = p1;
                p1 = p2;
                p2 = p3;
            }
        }
        ListNode *l1 = head->next, *l2 = p1, *l = head; // merge two lists    
        while(l1){ // len(l1) <= l2 
            l->next = l2;
            l = l->next;
            l2 = l2->next;
            l->next = l1;
            l = l->next;
            l1 = l1->next;
        } l->next = l2; 
    }
};

Partition List

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.
思路:双指针

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(head == NULL) return head;
        ListNode lessThanX(-1), notLessThanX(-1);
        ListNode *p = head, *p1 = &lessThanX, *p2 = &notLessThanX;
        while(p){
            if(p->val < x){
                p1->next = p;
                p1 = p1->next;
            } else {
                p2->next = p;
                p2 = p2->next;
            } p = p->next;
        } 
        p1->next = notLessThanX.next;
        p2->next = NULL;
        return lessThanX.next;
    }
};

Rotate List

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

For example:
Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.
注意:k可能大于链表长度

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(k < 1 || !head || head->next == NULL) return head; // at least 2 nodes
        ListNode *p1 = head, *p2 = p1;
        int len = 1;
        for(int i = 0; i < k; i++){
            if(p2->next){
                p2 = p2->next;
                len++;
            } else {
                k = k%len;
                len = 1;
                p2 = head;
                i = -1;
            }
        }
        while(p2->next != NULL){
            p1 = p1->next;
            p2 = p2->next;
        } p2->next = head;
        head = p1->next; // update head
        p1->next = NULL;
        return head;
    }
};

双链表

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值