Leetcode 链表总结1

148. Sort List链表排序

    1. 归并(top down)
      使用快慢指针将链表分成两个部分;
      l1, l2 = split(l)



      l1',l2' = sortList(l1), sortList(l2) (递归,当l1和l2只有一个节点时,sortList返回本身),时间复杂度logN,空间复杂度logN
      merge(l1', l2'),时间复杂度N

下面的代码需要注意,swap(l1, l2),交换的指针指向的内容, 包括val和next!

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(head==nullptr|| head->next==nullptr)
            return head;
        ListNode* slow = head, *fast = head->next;
        while(fast!=nullptr && fast->next!=nullptr){
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* mid = slow->next;
        slow->next = nullptr;
        return mergeList(sortList(head), sortList(mid));
    }
private:
    ListNode* mergeList(ListNode* l1, ListNode* l2){
        ListNode dummy(0); //定义在栈上,不用关心内存的问题
        ListNode* tail = &dummy;
        while(l1 && l2){
            if(l1->val > l2->val){
                swap(l1, l2);
            }
            tail->next = l1;
            l1 = l1->next;
            tail = tail->next;
        }
        if(l1) tail->next = l1;
        if(l2) tail->next = l2;
        return dummy.next;
    }
};
    1. 归并(bottom up) -- O(1)的空间复杂度
  • 两两merge,需要有一个split函数,将链表进行分割,按照1,2,4,8,16...的间隔进行分割;
  • merge的时候不止传回merge后链表的头结点,还需要传回尾节点,以此来实现滚动;
class Solution {
public:
  ListNode* sortList(ListNode* head) {
    // 0 or 1 element, we are done.
    if (!head || !head->next) return head;
    
    int len = 1;
    ListNode* cur = head;
    while (cur = cur->next) ++len;
    
    ListNode dummy(0);
    dummy.next = head;
    ListNode* l;
    ListNode* r;
    ListNode* tail;
    for (int n = 1; n < len; n <<= 1) {      
      cur = dummy.next; // partial sorted head
      tail = &dummy;
      while (cur) {
        l = cur;
        r = split(l, n);
        cur = split(r, n);
        auto merged = merge(l, r);
        tail->next = merged.first;
        tail = merged.second;
      }
    }      
    return dummy.next;
  }
private:
  // Splits the list into two parts, first n element and the rest.
  // Returns the head of the rest.
  ListNode* split(ListNode* head, int n) {    
    while (--n && head)
      head = head->next;
    ListNode* rest = head ? head->next : nullptr;
    if (head) head->next = nullptr;
    return rest;
  }
  
  // Merges two lists, returns the head and tail of the merged list.
  pair<ListNode*, ListNode*> merge(ListNode* l1, ListNode* l2) {
    ListNode dummy(0);
    ListNode* tail = &dummy;
    while (l1 && l2) {
      if (l1->val > l2->val) swap(l1, l2);
      tail->next = l1;
      l1 = l1->next;
      tail = tail->next;
    }
    tail->next = l1 ? l1 : l2;
    while (tail->next) tail = tail->next;
    return {dummy.next, tail};
  }
};
  • 链表快排
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        
        if(head == NULL)    return head;
        Quick_Sort(head, NULL);
        return head;    
    }
    
    void Quick_Sort(ListNode* head, ListNode* end)
    {
        if (head != end && head->next != end)
        {
            ListNode* ptr = head;
            while(ptr->next != end) ptr = ptr->next; // 取链表最后一个元素作为主元
            int pivot = ptr->val;
            
            ListNode* p1 = head;
            ListNode* p2 = head;
            
            while(p2->next != end)
            {
                if(p2->val < pivot)
                {
                    int temp = p2->val;
                    p2->val = p1->val;
                    p1->val = temp;
                    p1 = p1->next;
                }
                p2 = p2->next;
            }
            p2->val = p1->val;
            p1->val = pivot;
            Quick_Sort(head, p1); // 此时 p1 指向 pivot,前一半链表头为 head,尾为 p1
            Quick_Sort(p1->next, end); // 后一半链表头为 p1->next,尾为 end
        }
    }
};

25.Reverse Nodes in k-Group

  • 在每k个链表内进行逆序
  • k个链表内的数逆序方式为,如下图:
    将第一个数设为tail(因为逆序后肯定会排到最后面)
    设置curr节点,从tail->next一直到last前一个节点,
    将每个curr移动到prev->next,作为第一个节点
    每次移动curr前,设置next节点,也就是curr->next,实现curr滚动


class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(head == NULL) return NULL;
        ListNode dummy(0);
        dummy.next = head;
        ListNode* prev = &dummy;
        while(prev != NULL){
            prev = reverse(prev,k);
        }
        return dummy.next;
    }   
private:
    ListNode* reverse(ListNode* prev, int k){
        ListNode* last = prev;
        for(int i=0; i<k+1; ++i){
            last = last->next;
            if(last == NULL && i!=k)
                return NULL;
        } //设置要进行逆序的区间,到last就截止
        ListNode* tail = prev->next;
        ListNode* curr = tail->next;
        while(curr != last){
            ListNode* next = curr->next;
            curr->next = prev->next;
            prev->next = curr;
            tail->next = next;
            curr = next;
        }
        return tail;
    }
};

使用stack的方式

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(head == NULL) return NULL;
        ListNode dummy(0);
        dummy.next = head;
        stack<ListNode *> sliding;
        ListNode* curr = &dummy;
        ListNode* next = curr->next;
        while(next != NULL){
           for(int i=0; i<k && next!=NULL; ++i){
                sliding.push(next);
                next = next->next;
            }
            if(sliding.size() != k) return dummy.next; 
            
            while(sliding.size() != 0){
                curr->next = sliding.top();
                sliding.pop();
                curr = curr->next;
            }
            curr->next = next;
        }
        return dummy.next;
    }
};

141. Linked List Cycle

https://leetcode.com/problems/linked-list-cycle/
这道题是二刷的题目。我把之前的代码删掉,重新做了一遍,发现这次想出来的方法更简单。解法二和解法三是之前在雁栖湖做的。
解法一:将已经遍历过的节点的value都置为INT_MAX,接着往后遍历,如果后面出现节点值为INT_MAX,那么说明链表中存在Cycle

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* p;
        p = head;
        while(p != NULL){
            p->val = INT_MAX;
            p = p -> next;
            if(p != NULL && p->val == INT_MAX)
                return true;
        }
        return false;
    }
};

解法二:使用哈希set的方法,将遍历过的节点插入到哈希表中

class Solution {
public:
    bool hasCycle(ListNode *head) {
        unordered_set<ListNode*> visited;
        while(head){
            if(visited.count(head))
                return true;
            visited.insert(head);
            head = head->next;
        }
        return false;
    }
};

解法三:使用快慢指针,快指针一次走两步,慢指针一次走一步,有环的话快指针会在环里追到慢指针。

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

*146. LRU Cache

https://leetcode.com/problems/lru-cache/

这是一道很难的设计题,需要使用多种容器进行操作。我对C++容器的使用还是比较陌生,所以解起来难度比较大,希望自己下次再看到这道题的时候,整理一下C++容器的用法,加深理解。

[http://zxi.mytechroad.com/blog/hashtable/leetcode-146-lru-cache/]
class LRUCache {
public:
    LRUCache(int capacity) {
        capacity_ = capacity;
    }
    
    int get(int key) {
        const auto it = m_.find(key);
        // If key does not exist
        if (it == m_.cend()) return -1;
 
        // Move this key to the front of the cache
        cache_.splice(cache_.begin(), cache_, it->second);
        return it->second->second;
    }
    
    void put(int key, int value) {        
        const auto it = m_.find(key);
        
        // Key already exists
        if (it != m_.cend()) {
            // Update the value
            it->second->second = value;
            // Move this entry to the front of the cache
            cache_.splice(cache_.begin(), cache_, it->second);
            return;
        }
        
        // Reached the capacity, remove the oldest entry
        if (cache_.size() == capacity_) {
            const auto& node = cache_.back();
            m_.erase(node.first);
            cache_.pop_back();
        }
        
        // Insert the entry to the front of the cache and update mapping.
        cache_.emplace_front(key, value);
        m_[key] = cache_.begin();
    }
private:
    int capacity_;
    list<pair<int,int>> cache_;
    unordered_map<int, list<pair<int,int>>::iterator> m_;
};
 
/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

725. Split Linked List in Parts

https://leetcode.com/problems/split-linked-list-in-parts/

这一题比较无聊,主要是考察list的操作

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<ListNode*> splitListToParts(ListNode* root, int k) {
        vector<ListNode*> ans(k, nullptr);
        
        int inputLength = 0;
        for(ListNode* head = root; head; head = head->next){
            ++inputLength;
        }
        
        int numOfEachElement = inputLength / k;
        int numOfAdd = inputLength % k;
        vector<int> sizeOfEachElement;
        for(int i=0; i<k; ++i){
            if(numOfAdd-- > 0){
                sizeOfEachElement.push_back(numOfEachElement+1);
            }
            else{
                sizeOfEachElement.push_back(numOfEachElement);
            }
        }
                
        ListNode* p = nullptr;
        ListNode* head = root;
        for(int j=0; j<k; ++j){
            if(sizeOfEachElement[j] == 0){
                continue;
            }
            ans[j] = head;
            while(--sizeOfEachElement[j] >= 0){
                p = head;
                head = head->next;
            }
            p->next = nullptr;
        }
        
        return ans;
    }
};

817. Linked List Components

https://leetcode.com/problems/linked-list-components/
题目大意:给你一个链表,再给你一些合法的节点,问你链表中有多少个连通分量(所有节点必须合法)。
解法一:把vector G 初始化为unordered_set,方便查找。然后遍历所有节点,倘若当前节点在G中,并且邻节点为空或值不在G中,那么为不连通

class Solution {
public:
    int numComponents(ListNode* head, vector<int>& G) {
        unordered_set<int> g(G.begin(), G.end());
        
        int ans = 0;
        while(head){
            if(g.count(head->val) && (!head->next || !g.count(head->next->val)))
                ans++;
            head = head->next;
        }
        return ans;
    }
};

解法二:Graph Traversal using DFS(By huahua)

class Solution {
public:
  int numComponents(ListNode* head, vector<int>& G) {
    unordered_set<int> f(G.begin(), G.end());
    unordered_map<int, vector<int>> g;
    int u = head->val;
    while (head->next) {
      head = head->next;
      int v = head->val;
      if (f.count(v) && f.count(u)) {
        g[u].push_back(v);
        g[v].push_back(u);
      }
      u = v;
    }
    int ans = 0;
    unordered_set<int> visited;
    for (int u : G) {      
      if (visited.count(u)) continue;
      ++ans;
      dfs(u, g, visited);
    }
    return ans;
  }
private:
  void dfs(int cur, unordered_map<int, vector<int>>& g, unordered_set<int>& visited) {
    if (visited.count(cur)) return;
    visited.insert(cur);
    for (const int next : g[cur])
      dfs(next, g, visited);    
  }
};

21. Merge Two Sorted Lists

dummy作用:在栈上申请一个ListNode对象,dummy.next 指向head

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode dummy(0);
        ListNode* tail=&dummy;
        
        while(l1 && l2){
            if(l1->val < l2->val){
                tail->next = l1;
                l1 = l1->next;
            }
            else{
                tail->next = l2;
                l2 = l2->next;
            }
            tail = tail->next;
        }
        if(l1) tail->next = l1;
        if(l2) tail->next = l2;
        
        return dummy.next;
    }  
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容