程序員面試之算法備忘錄(三) | 鏈表

前言


本文是題主準備面試時記錄下的筆記整理而來,稍顯粗陋,還請各位擼友勿噴哈!

Topic


  • 目標

    • 熟練使用常用數據結構的基本操作
    • 加深對常用算法與技巧的理解
    • 面試
  • 參考

    • 《程序員面試金典》
    • 《劍指offer》
    • Leetcode
    • 《結構之法 --July》

鏈表篇


1_1.removeDuplicateFromLinklist

1.問題描述

  • 未排序鏈表
  • 移除重復節點

2.策略一:

  • 對鏈表排序
  • 通過快慢指針消除重復元素

3.策略二:

  • 哈希表儲存元素
  • 遍歷鏈表
  • 遇到重復元素,從鏈表中刪去.

4.注意指針的命名方式

5.代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:      
                        (1)問題描述
                            A.已排序鏈表
                            B.移除重復節點
    > Conclusion:          
                        (1)策略一:
                            A.鏈表排序
                            B.通過快慢指針消除重復元素
                        
                        (2)策略二:
                            A.哈希表儲存元素
                            B.遍歷鏈表
                            C.遇到重復元素,從鏈表中刪去.
    
    > Author:           rh_Jameson
    > Created Time:     2014年12月16日 星期二 11時36分52秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<ctime>
#include "LinkList.h"

using namespace std;


class Solution {
    
public:

//=================哈希實現:空間O(M),時間O(N)====================//
    void removeDuplicatesFromLinklistByHash(ListNode *head){
        //判空
        if(head == NULL){
            cout << "空鏈表" << endl;
        }
        //哈希實現
        map<int, int> unique_map;
        ListNode *p = head, *tmp = new ListNode(0);
        ListNode *q = head->next;
        unique_map[p->val] = p->val;
        
        for(q = head->next; q != NULL; q = q->next){
            if(unique_map.count(q->val)){
                p->next = q->next;
                tmp = q;
                q = p;
                delete tmp;
            }
            else{
                unique_map[q->val] = q->val;
                p = p->next;
            }
        }
//===================while形式====================//
/*
        while(q != NULL){
            if(unique_map.count(q->val)){
                p->next = q->next;
                tmp = q;
                q = q->next;
                delete tmp;
            }
            else{
                unique_map[q->val] = q->val;
                p = p->next;
                q = q->next;
            }
        }
*/
    }
//=================================兩個指針實現版=======================================//
    ListNode *deleteDuplicates(ListNode *head) {        //頭結點是第一個結點
        if(head == NULL){
            return NULL;
        }
        ListNode *pre = head, *cur,*tmp = new ListNode(0);
       
        for(cur = head->next; cur; cur = cur->next){
            if(cur->val == pre->val){
                pre->next = cur->next;
                delete cur;             //不安全,待優化
            }
            else{
                pre = cur;
            }
        }
        return head;
        
    }
};

#endif


1_2.return_nth_node_from_end_of_list


1.問題描述:

  • 單向鏈表
  • 找到或者刪除倒數第k個結點

2.解決策略

  • 策略一:轉成正數第m個

    • 一輪遍歷求鏈表長度
    • 倒數第k個,即正數 m = n + 1 - k 個
    • 遍歷m個結點,返回第m個結點
  • 策略二:快慢指針

    • 快指針先走k步
    • 接著快慢指針同時向前移動
    • 直到鏈表遍歷結束
    • 返回慢指針指向的結點

3.調bug須知

  • 邊界測試用例:

    • 空鏈表
    • k > n
    • k < 0
    • 單結點鏈表
  • 傳進來的頭結點:

    • 其實是第一個元素
    • 最好自己新建個頭結點
    • 避免第一個結點特殊處理
  • 刪除結點如果是head結點:

    • A.只能想到加個if
    • B.返回head->next

4.代碼示例

/*************************************************************************
    > File Name:        Solution.h
    > Description:      
                        (1)問題描述:
                            A.單向鏈表
                            B.找到或者刪除倒數第k個結點
    > Conclusion:        
                        (1)策略一:轉成正數第m個
                            A.一輪遍歷求鏈表長度
                            B.倒數第k個,即正數 m = n + 1 - k 個
                            C.遍歷m個結點,返回第m個結點
                            
                        (2)策略二:快慢指針
                            A.快指針先走k步
                            B.接著快慢指針同時向前移動
                            C.直到鏈表遍歷結束
                            D.返回慢指針指向的結點
                        
                        (3)邊界測試用例:
                            A.空鏈表
                            B.k > n
                            C.k < 0
                            D.單結點鏈表

                        (4)傳進來的頭結點:
                            A.其實是第一個元素
                            B.最好自己新建個頭結點
                            C.避免第一個結點特殊處理

                        (1)刪除結點如果是head結點:
                            A.只能想到加個if
                            B.返回head->next
    
    > Author:           rh_Jameson
    > Created Time:     2014年12月16日 星期二 20時07分49秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>
#include "LinkList.h"
using namespace std;

class Solution {
public:
//==================快慢指針實現====================//
    void getNthNode(ListNode *head,int k){
        if(head == NULL){
            cout << "空鏈表" << endl;
            return;
        }
        if(k <= 0){
            cout << "k值小于等于0,不合法" << endl;
            return;
        }
        ListNode *p_fast = new ListNode(0);
        p_fast->next = head;
        //
        for(int i = 0; i < k; i++){
            //處理k > len的情況
            if(p_fast == NULL){
                cout << "k大于鏈表長度!" << endl;
                return;
            }
            p_fast = p_fast->next;
        }
        ListNode *cur = new ListNode(0);
        cur->next = head;
        while(p_fast != NULL){
            p_fast = p_fast->next;
            cur = cur->next;
        }
        cout << "倒數第k個值是" << cur->val << endl;

    }
//================轉換成正數第m個===================//
    void getNthNodeByM_th(ListNode *head,int k){
        if(head == NULL){
            cout << "空鏈表" << endl;
            return;
        }
        int len = 0;
        ListNode *cur = head;
        //求鏈表長度 
        while(cur != NULL){
            ++len;
            cout << len << "\t";
            cur = cur->next;
        }
        cout << endl;
        if(k > len || k < 1){
            cout << "k值不合法" << endl;
            return;
        }
        cout << endl;
        cout << head->val << endl; 
        cur = head;     //重置
        //遍歷到第m個結點
        int m_th = len + 1 - k;
        for(int i = 1; i < m_th; ++i){
            cur = cur->next;
        }
        cout << "倒數第k個的值是" << cur->val << endl;
    } 
};

#endif

1_3.del_mid_node_from_list

1.問題描述:

  • 單向鏈表
  • 只能訪問某結點
  • 且該結點要被移除

2.策略:

  • 將該結點的下一個結點拷貝到該結點上
  • 注意邊界

3.邊界測試用例:

  • 空鏈表
  • 單結點鏈表
  • 要刪除結點是最后一個結點

4.代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:     
                        (1)問題描述:
                            A.單向鏈表
                            B.只能訪問某結點
                            C.且該結點要被移除
    
    > Conclusion:          
                        (1)策略:
                            A.將該結點的下一個結點拷貝到該結點上
                            B.注意邊界

                        (2)邊界測試用例:
                            A.空鏈表
                            B.單結點鏈表
                            C.要刪除結點是最后一個結點
                        

    > Author:           rh_Jameson
    > Created Time:     2014年12月16日 星期二 21時58分22秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>

#include"LinkList.h"

using namespace std;

class Solution {
public:
    void removeMidNode(ListNode *midNode){
        if(midNode == NULL || midNode->next == NULL){
            cout << "輸入不合法" << endl;
        }
        *(midNode) = *(midNode->next);
        //midNode->val = midNode->next->val;
        //midNode->next = midNode->next->next;
    }
};

#endif

1_4.partition_list


1.問題描述:

  • 鏈表分割成兩部分
  • 以給定值x為基準
  • 小于x的結點在前
  • 大于或等于x的結點在后

2.解決策略

  • 策略一:另開鏈表

    • 新建一鏈表
    • 小于x,從原鏈表中刪除
    • 并將其插入新建鏈表中
    • 結束一輪遍歷后,連接兩條鏈表
  • 策略二:頭插處理

    • 遍歷鏈表
    • 比x小的結點,從鏈表中刪除
    • 再用頭插法插入鏈表

3.關于策略二:

  • leetcode上不能使用頭插法
  • 書上也木有相似解法
  • 原因:頭插法順序變化了
  • 但題目沒說要按原順序啊~~

4.頭結點(指第一個元素)改變的情況:

  • 需改為指針的引用ListNode* &node
  • 第一個元素往往要特殊對待

5.代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:      
                       (1)問題描述:
                            A.鏈表分割成兩部分
                            B.以給定值x為基準
                            C.小于x的結點在前
                            D. 大于或等于x的結點在后
    > Conclusion:       
                       (1)策略一:另開鏈表
                            A.新建一鏈表
                            B.小于x,從原鏈表中刪除
                            C.并將其插入新建鏈表中
                            D. 結束一輪遍歷后,連接兩條鏈表

                       (2)策略二:頭插處理
                            A.遍歷鏈表
                            B.比x小的結點,從鏈表中刪除
                            C.再用頭插法插入鏈表
    
                       (3)關于策略二:
                            A.leetcode上不能使用頭插法
                            B.書上也木有相似解法
                            C.原因:頭插法順序變化了
                            D.但題目沒說要按原順序啊~~ 
    
                       (4)頭結點(指第一個元素)改變的情況:
                            A.需改為指針的引用ListNode* &node 
                            B.第一個元素往往要特殊對待
    
    > Author:           rh_Jameson
    > Created Time:     2014年12月17日 星期三 13時40分42秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>
#include "LinkList.h"
using namespace std;

class Solution {
public:
//=====================另開一個鏈表======================//
    ListNode *partitionByNewList(ListNode *head, int x) {
        if(head == NULL){
            return NULL;
        }
        if(head->next == NULL){
            return head;
        }
        //指針太多,暈死~~待優化ing
        ListNode *cur = head, 
                 *newList = new ListNode(x),                //新鏈表頭結點
                 *new_cur = newList;                        //新鏈表游標
        
        ListNode *pre = new ListNode(0), 
                 *head_node = new ListNode(0),              //原鏈表頭結點
                 *tmp;
        pre->next = cur;
        head_node->next = head;
        //遍歷,小于x的從原鏈表中刪除,加入新鏈表
        while(cur != NULL){
            if(cur->val < x){
                tmp = cur;
                pre->next = cur->next;
                if(cur == head_node->next){                 //待刪元素是第一個元素特別處理
                    head_node->next = cur->next;
                }
                cur = cur->next;
                
                tmp->next = NULL;
                new_cur->next = tmp;
                new_cur = new_cur->next;
            }
            else{
                pre = cur;
                cur = cur->next;
            }
        }
        //連接兩個鏈表
        new_cur->next = head_node->next;
        return newList->next;                   //注意返回第一個元素而非頭結點
    }


//================================頭插法實現============================//
    void partition(ListNode * &head, int x){    //頭結點變化情況下,須聲明為ListNode* &head或ListNode **head
        if(head == NULL){
            cout << "空鏈表" << endl;
            return;
        }
        if(head->next == NULL){
            cout << "單節點鏈表" << endl;
            return;
        }
        ListNode *cur = head,
                 *pre = new ListNode(0),
                 *head_node = new ListNode(0);
        pre->next = head;
        head_node->next = head;

        while(cur != NULL){
            if(cur->val < x && cur != head){
                pre->next = cur->next;
                cur->next = head_node->next;
                head_node->next = cur;
                cur = pre->next;
            }
            else{
                pre = cur;
                cur = cur->next;
            }
        }
        head = head_node->next;
        
    } 
};

#endif

1_5.add_two_num


1.問題描述:

  • 有兩個鏈表,每個結點含一個數位
  • 兩個鏈表代表兩個整數
  • 求兩個整數之和
  • 鏈表形式返回結果
  • 數位分反向存放與正向存放兩種情況

2.反向策略一:轉為整型

  • 聲明兩個加數變量
  • 遍歷兩個鏈表
  • 將結點的數位轉到加數變量中
  • 相加賦值到sum變量
  • 鏈表形式逐位輸出
  • 關鍵公式:
    • a.v += p->data * pow(10,i)

3.反向策略二:諸位添加

  • 加入進位標志
  • 將原有的兩個鏈表對應結點值相加
  • 所得之和插入新建鏈表中
  • 進位標志為1時,注意所得之和需加1

4.coding遇到的問題

  • 策略二需注意一下問題
    • 其中一個鏈表已空,而另一鏈表還有元素
    • 當兩個鏈表為空時,進位標志仍為1
    • 代碼優化與復用,防止重復代碼
  • 新建結點相關:
    • 如果可以,不用臨時結點
    • 減少相關指針,防止指針太多,影響思路
  • 代碼路徑與優化:
    • 存在多條代碼路徑時,需注意優化
    • 盡可能找出路徑之間的重合與關聯
    • 縮減/優化代碼
  • 正向存放待實現~~

5.代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:     
                        (1)問題描述:
                            A.有兩個鏈表,每個結點含一個數位
                            B.兩個鏈表代表兩個整數
                            C.求兩個整數之和
                            D.鏈表形式返回結果
                            E.數位分反向存放與正向存放兩種情況
    > Conclusion:          
                        (1)反向策略一:轉為整型
                            A.聲明兩個加數變量
                            B.遍歷兩個鏈表
                            C.將結點的數位轉到加數變量中
                            D.相加賦值到sum變量
                            E.鏈表形式逐位輸出
                            F.關鍵公式:
                                a.v += p->data * pow(10,i)

                        (2)反向策略二:諸位添加
                            A.加入進位標志
                            B.將原有的兩個鏈表對應結點值相加
                            C.所得之和插入新建鏈表中
                            D.進位標志為1時,注意所得之和需加1
                     
                        (3)策略二需注意一下問題
                            A.其中一個鏈表已空,而另一鏈表還有元素
                            B.當兩個鏈表為空時,進位標志仍為1
                            C.代碼優化與復用,防止重復代碼
                        
                        (4)新建結點相關:
                            A.如果可以,不用臨時結點
                            B.減少相關指針,防止指針太多,影響思路
                        
                        (5)代碼路徑與優化:
                            A.存在多條代碼路徑時,需注意優化
                            B.盡可能找出路徑之間的重合與關聯
                            C.縮減/優化代碼

                        (6)正向存放待實現~~

    > Author:           rh_Jameson
    > Created Time:     2014年12月18日 星期四 11時48分40秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>

using namespace std;

class Solution {
public:
//=========================最終版:AC,省去多余指針=========================//
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        ListNode *res_head = new ListNode(0), *cur = res_head;
        int flag = 0, tmp_value = 0;
        while(l1 != NULL || l2 != NULL || flag){
            //計算和
            if(l1 != NULL){
                tmp_value += l1->val;
                l1 = l1->next;
            }
            if(l2 != NULL){
                tmp_value += l2->val;
                l2 = l2->next;
            }
            tmp_value += flag;
            //判斷是否有進位
            if(tmp_value >= 10){
                flag = 1;
                tmp_value %= 10;
            }
            else{
                flag = 0;
            }
            //加入新結點
            cur->next = new ListNode(tmp_value);
            cur = cur->next;
            tmp_value = 0;
        }
        return res_head->next;
    }

//=========================優化版2:AC,代碼細節優化========================//
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        //判兩鏈表是否都為空
        if(l1 == NULL && l2 == NULL){
            return NULL;
        }
        ListNode *res_head = new ListNode(0), *cur = res_head, *tmp;
        ListNode *cur_l1 = l1, *cur_l2 = l2;
        int flag = 0, tmp_value = 0;
        while(cur_l1 != NULL || cur_l2 != NULL || flag){
            //計算和
            if(cur_l1 != NULL){
                tmp_value += cur_l1->val;
                cur_l1 = cur_l1->next;
            }
            if(cur_l2 != NULL){
                tmp_value += cur_l2->val;
                cur_l2 = cur_l2->next;
            }
            tmp_value += flag;
            //判斷是否有進位
            if(tmp_value >= 10){
                flag = 1;
                tmp_value %= 10;
            }
            else{
                flag = 0;
            }
            //加入新結點
            cur->next = new ListNode(tmp_value);
            cur = cur->next;
            tmp_value = 0;
        }
        return res_head->next;
    }


//====================優化版:AC,代碼復用優化==============================//
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        //判兩鏈表是否都為空
        if(l1 == NULL && l2 == NULL){
            return NULL;
        }
        
        ListNode *res_head = new ListNode(0), *cur = res_head, *tmp;
        ListNode *cur_l1 = l1, *cur_l2 = l2;
        int flag = 0;
        int tmp_value;
        
        while(cur_l1 != NULL || cur_l2 != NULL){
            //l1鏈表空,l2還有結點的情況
            if(cur_l1 == NULL){
                tmp_value = cur_l2->val + flag;
                cur_l2 = cur_l2->next;
            }
            //l2鏈表空,l1還有結點的情況
            else if(cur_l2 == NULL){
                tmp_value = cur_l1->val + flag;
                cur_l1 = cur_l1->next;
            }
            //l1,l2均有鏈表的情況
            else{
                tmp_value = cur_l1->val + cur_l2->val + flag;
                cur_l1 = cur_l1->next;
                cur_l2 = cur_l2->next;
            }
            //判斷是否有進位
            if(tmp_value >= 10){
                flag = 1;
                tmp_value %= 10;
            }
            else{
                flag = 0;
            }
            //加入新結點
            tmp = new ListNode(tmp_value);
            tmp->next = NULL;
            cur->next = tmp;
            cur = cur->next;
        }
        if(flag == 1){
            tmp = new ListNode(1);
            tmp->next = NULL;
            cur->next = tmp;
            flag = 0;
        }
        return res_head->next;
    }

//====================原始版:AC,但代碼冗長,重復太多======================//
    void addTwoNumbers(ListNode *l1, ListNode *l2) {
       ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        if(l1 == NULL && l2 == NULL){
            return NULL;
        }
        
        ListNode *res_head = new ListNode(0), *cur = res_head, *tmp, *new_node;
        ListNode *cur_l1 = l1, *cur_l2 = l2;
        int flag = 0;
        int tmp_value;
        while(cur_l1 != NULL || cur_l2 != NULL){
            //l1鏈表空,l2還有結點的情況
            if(cur_l1 == NULL){
                tmp = cur_l2;
                while(flag == 1){
                    cur_l2->val += flag;
                    if(cur_l2->val >= 10){
                        cur_l2->val %= 10;
                        if(cur_l2->next != NULL){
                            cur_l2 = cur_l2->next;
                        }
                        else{
                            new_node = new ListNode(1);
                            cur_l2->next = new_node;
                            flag = 0;
                        }
                        
                    }
                    else{
                        flag = 0;
                    }
                }
                cur->next = tmp;
                break;
            }
            //l2鏈表空,l1還有結點的情況
            if(cur_l2 == NULL){
                tmp = cur_l1;
                while(flag == 1){
                    cur_l1->val += flag;
                    if(cur_l1->val >= 10){
                        cur_l1->val %= 10;
                        if(cur_l1->next != NULL){
                            cur_l1 = cur_l1->next;
                        }
                        else{
                            new_node = new ListNode(1);
                            cur_l1->next = new_node;
                            flag = 0;
                        }
                    }
                    else{
                        flag = 0;
                    }
                }
                cur->next = tmp;
                break;
            }
            
            if(flag == false){
                tmp_value = cur_l1->val + cur_l2->val;
            }
            else{
                tmp_value = cur_l1->val + cur_l2->val + flag;
                flag = 0;
            }
            
            if(tmp_value >= 10){
                flag = 1;
                tmp_value %= 10;
            }
            tmp = new ListNode(tmp_value);
            tmp->next = NULL;
            cur->next = tmp;
            cur = cur->next;
            
            cur_l1 = cur_l1->next;
            cur_l2 = cur_l2->next;
        }
        if(flag == 1){
            new_node = new ListNode(1);
            new_node->next = NULL;
            cur->next = new_node;
            flag = 0;
          
        }
        return res_head->next;
    }
        
};

#endif



1_6.link_list_cycle


1.問題描述:

  • 鏈表
  • 判斷是否是有環鏈表
  • 返回環路開頭結點

2.鏈表相關注意:

  • 做鏈表題目時,一定要理清思路后再實現
  • 注意邊界用例特殊情況
  • 最好能畫圖在紙上確保木有問題了再轉成代碼

3.策略 & 思路:快慢指針

  • 設定一快一慢指針,p_fast & p_slow
  • p_slow走一步,p_fast走兩步
  • 設p_slow走k步進入環,到達環的入口接點
    • 此時,p_slow = k, p_fast = 2k
    • 快慢相距 (p_fast - p_slow)k 步
  • 設環總長度為L,則快慢反向相距L - k步
  • 快慢指針每移動一位,反向相距長度就減一
    • 移動L - k次后,快慢相遇
    • 此時慢指針在環內走了L - k步
  • 即慢指針距離入口結點: L - (L - k) = k
  • 快慢相遇后,慢指針指回頭結點
  • 快慢繼續向前推進
  • 當快慢指針的指向值相等時,即是環路開頭結點

4.代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:     
                        (1)問題描述:
                            A.鏈表
                            B.判斷是否是有環鏈表
                            C.返回環路開頭結點
    
    > Conclusion:          
                        (1)策略 & 思路:快慢指針
                            A.設定一快一慢指針,p_fast & p_slow
                            B.p_slow走一步,p_fast走兩步
                            
                            C.設p_slow走k步進入環,到達環的入口接點
                                a.此時,p_slow = k, p_fast = 2k
                                b.快慢相距 (p_fast - p_slow)k 步
                            
                            D. 設環總長度為L,則快慢反向相距L - k步
                            
                            E.快慢指針每移動一位,反向相距長度就減一
                                a.移動L - k次后,快慢相遇
                                b.此時慢指針在環內走了L - k步
                            
                            F.即慢指針距離入口結點:
                                    L - (L - k) = k
                            G.快慢相遇后,慢指針指回頭結點
                            H.快慢繼續向前推進
                            I.當快慢指針的指向值相等時,即是環路開頭結點
                        
                        (2)鏈表相關注意:
                            A.做鏈表題目時,一定要理清思路后再實現
                            B.注意邊界用例特殊情況
                            C.最好能畫圖在紙上確保木有問題了再轉成代碼
                        
                        (1)問題描述:
                            A.
                            B.
                            C.
                            D. 
    > Author:           rh_Jameson
    > Created Time:     2014年12月19日 星期五 17時41分07秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>

using namespace std;

class Solution {
public:
//===================快慢指針返回環入口結點:自增頭結點版本====================//
    ListNode *detectCycle(ListNode *head) {
        //定義頭結點,快慢指針
        ListNode *head_node = new ListNode(0);       //p_slow = NULL OK?
        head_node->next = head;
        ListNode *p_slow = head_node, *p_fast = head_node;
        
        while(p_slow != p_fast || p_slow == head_node){
            //如p_fast已指向鏈表尾部,返回NULL
            if(p_fast->next == NULL || p_fast->next->next == NULL){
                return NULL;
            }
            //快慢向前推進
            p_slow = p_slow->next;
            p_fast = p_fast->next->next;
        }
        //慢指針重置
        p_slow = head_node;
        //快慢指針重新向前推進,直至相遇
        while(p_slow != p_fast){
            p_slow = p_slow->next;
            p_fast = p_fast->next;
        }
        return p_slow;
    }
//============快慢指針返回環入口結點:無頭結點優化版本(非本人代碼)=============//
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        ListNode *detect = head;
        while(fast != NULL && fast->next != NULL){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == detect) return detect;
            if(slow == fast) detect = detect->next;
        }
        return NULL;
    }



//========================判斷鏈表是否是有環鏈表========================//
    bool hasCycle(ListNode *head) {
        ListNode *head_node = new ListNode(0);       //p_slow = NULL OK?
        head_node->next = head;
        ListNode *p_slow = head_node, *p_fast = head_node;
        
        while(p_slow != p_fast || p_slow == head_node){
            if(!p_fast->next || !p_fast->next->next){
                return false;
            }
            p_slow = p_slow->next;
            p_fast = p_fast->next->next;
        }
        return true;
    }

};

#endif


1_7.isPalindrome

1.問題描述:

  • 檢測鏈表是否為回文

2.策略一:數組保存原鏈表

  • 逆置鏈表
  • 同時將結點存入數組
  • 遍歷新鏈表
  • 同時與數組相應位置比較
  • 相等移入下一位置
  • 不等返回false

3.策略二:快慢指針實現

  • 快慢指針進行一輪遍歷
  • 慢指針將鏈表分段
  • 對前半段進行反轉
  • 遍歷判斷前后兩段元素是否相等

4.策略二優化:

  • 慢指針邊移動時,邊反轉元素
  • 代碼攪在一起也不是一件好事
  • 代碼出錯率增加

5.代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:      
                        (1)問題描述:
                            A.檢測鏈表是否為回文
    
    > Conclusion:      
                        (1)策略一:數組保存原鏈表
                            A.逆置鏈表
                            B.同時將結點存入數組
                            C.遍歷新鏈表
                            D.同時與數組相應位置比較
                            E.相等移入下一位置
                            F.不等返回false

                        (2)策略二:快慢指針實現
                            A.快慢指針進行一輪遍歷
                            B.慢指針將鏈表分段
                            C.對前半段進行反轉
                            D. 遍歷判斷前后兩段元素是否相等

                        (3)策略二優化:
                            A.慢指針邊移動時,邊反轉元素
                            B.代碼攪在一起也不是一件好事
                            C.代碼出錯率增加
    
    > Author:           rh_Jameson
    > Created Time:     2014年12月18日 星期四 17時52分27秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include "LinkList.h"

using namespace std;

class Solution {
public:
//==========================優化(有潛在邊界問題):不使用使用額外數組判斷回文======================//
    void isPalindrome(ListNode *head){
        if(head == NULL){
            cout << "NULL LinkList!" << endl;
            return;
        }
        if(head->next == NULL){
            cout << "single node LinkList!" << endl;
            return;
        }
        if(head->next->next == NULL){
            if(head->val != head->next->val){
                cout << "it is not a Palindrome" << endl;
                return;
            }
            else{
                cout << "it is a Palindrome" << endl;
                return;
            }
        }
        //快慢指針遍歷鏈表,同時反轉p_slow前面的結點
        ListNode *p_slow = head, *p_fast = head->next;
        ListNode *pre = NULL, *cur = head, *p_next; 
        while(p_fast != NULL){              
            p_slow = p_slow->next;
            p_fast = p_fast->next->next; 

            p_next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = p_next;
            if(p_fast->next == NULL){
                p_next = cur->next;
                cur->next = pre;
                pre = cur;
                cur = p_next;
                break;
            }
        }
        //鏈表長度為奇數的話,cur要向前移動一位
        if(p_fast == NULL){
            cur = cur->next;
        }
        //判斷兩段鏈表相應結點值是否相等
        while(pre != NULL){
            if(pre->val != cur->val){
                cout << "it is not a Palindrome" << endl;
                return;
            }
            pre = pre->next;
            cur = cur->next;
        }
        cout << "it is a Palindrome" << endl;
         
    }

//==========================不使用額外數組判斷回文======================//
    void isPalindromeByPtr(ListNode *head){
        if(head == NULL){
            cout << "NULL LinkList!" << endl;
            return;
        }
        if(head->next == NULL){
            cout << "single node LinkList!" << endl;
            return;
        }
        //快慢指針遍歷鏈表
        ListNode *p_slow = head, *p_fast = head->next;
        while(p_fast->next != NULL && p_fast->next->next != NULL){              //->next->next 這里發生段錯誤,需加入前半段才行
            p_slow = p_slow->next;
            p_fast = p_fast->next->next;
        }
        //反轉p_slow前半段結點
        ListNode *pre = NULL, *cur = head, *p_next;
        while(pre != p_slow){
            p_next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = p_next;
        }
        //鏈表長度為奇數的話,cur要向前移動一位
        if(p_fast->next != NULL){
            cur = cur->next;
        }
        //判斷兩段鏈表相應結點值是否相等
        while(pre != NULL){
            if(pre->val != cur->val){
                cout << "it is not a Palindrome" << endl;
                return;
            }
            pre = pre->next;
            cur = cur->next;
        }
        cout << "it is a Palindrome" << endl;
        
    } 
    
    
//==========================使用額外數組存放原順序======================//
    void isPalindromeByArray(ListNode *head){
        if(head == NULL){
            cout << "空鏈表" << endl;
            return;
        }
        if(head->next == NULL){
            cout << "單結點鏈表:" << head->val << endl;
            return;
        }
        vector<int> vec_pal;
        ListNode *pre = NULL, *cur = head, *p_next;
        while(cur != NULL){
            vec_pal.push_back(cur->val);
            p_next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = p_next;
        }
        head = pre;
        cur = pre;
        for(vector<int>::iterator i = vec_pal.begin(); i < vec_pal.end();++i){
            if(*i != cur->val){
                cout << "不是回文" << endl;
                return;
            }
            cur = cur->next;
        }
        cout << "是回文鏈表!" << endl;


    }
};

#endif


1_8.removeDuplicatesFromSortedList

代碼示例:

/*************************************************************************
    > File Name:        Solution.h
    > Description:      
                        (1)問題描述
                            A.已排序鏈表
                            B.移除重復節點
    > Conclusion:          
                        (1)策略一:
                            A.鏈表排序
                            B.通過快慢指針消除重復元素
                        
                        (2)策略二:
                            A.哈希表儲存元素
                            B.遍歷鏈表
                            C.遇到重復元素,從鏈表中刪去.
    
    > Author:           rh_Jameson
    > Created Time:     2014年12月16日 星期二 11時36分52秒
 ************************************************************************/

#ifndef _SOLUTION_H
#define _SOLUTION_H

#include<iostream>
#include<string>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<ctime>
#include "LinkList.h"

using namespace std;


class Solution {
    
public:

//=================哈希實現:空間O(M),時間O(N)====================//
    void removeDuplicatesFromLinklistByHash(ListNode *head){
        //判空
        if(head == NULL){
            cout << "空鏈表" << endl;
        }
        //哈希實現
        map<int, int> unique_map;
        ListNode *p = head, *tmp = new ListNode(0);
        ListNode *q = head->next;
        unique_map[p->val] = p->val;
        
        for(q = head->next; q != NULL; q = q->next){
            if(unique_map.count(q->val)){
                p->next = q->next;
                tmp = q;
                q = p;
                delete tmp;
            }
            else{
                unique_map[q->val] = q->val;
                p = p->next;
            }
        }
//===================while形式====================//
/*
        while(q != NULL){
            if(unique_map.count(q->val)){
                p->next = q->next;
                tmp = q;
                q = q->next;
                delete tmp;
            }
            else{
                unique_map[q->val] = q->val;
                p = p->next;
                q = q->next;
            }
        }
*/
    }
//=================================兩個指針實現版=======================================//
    ListNode *deleteDuplicates(ListNode *head) {        //頭結點是第一個結點
        if(head == NULL){
            return NULL;
        }
        ListNode *pre = head, *cur,*tmp = new ListNode(0);
       
        for(cur = head->next; cur; cur = cur->next){
            if(cur->val == pre->val){
                pre->next = cur->next;
                delete cur;             //不安全,待優化
            }
            else{
                pre = cur;
            }
        }
        return head;
        
    }
};

#endif


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容