數(shù)據(jù)結(jié)構(gòu)之鏈表篇

概念

1.和數(shù)組一樣,鏈表也是一種線性表。

2.從內(nèi)存結(jié)構(gòu)來看队秩,鏈表的內(nèi)存結(jié)構(gòu)是不連續(xù)的內(nèi)存空間,是將一組零散的內(nèi)存塊串聯(lián)起來昼浦,從而進行數(shù)據(jù)存儲的數(shù)據(jù)結(jié)構(gòu)馍资。

3.鏈表中的每一個內(nèi)存塊被稱為節(jié)點Node。節(jié)點除了存儲數(shù)據(jù)外关噪,還需記錄鏈上下一個節(jié)點的地址鸟蟹,即后繼指針next乌妙。

注意:掌握好技巧,會提高正確率戏锹,真正理解后冠胯,舉一反三。將常見算法深刻理解之后锦针,大多數(shù)鏈表算法將不再是你的障礙荠察。

技巧

  • 引用的理解
  • 引用丟失和內(nèi)存泄漏
  • 邊界判斷
  • 利用哨兵(啞節(jié)點)

引用的理解

例如: a和b節(jié)點中插入x節(jié)點
x.next = a.next;
a.next = x;
//其中x.next引用存儲了a的下個節(jié)點next 的內(nèi)存地址
// a.next 引用儲存的是x節(jié)點的內(nèi)存地址

引用丟失和內(nèi)存泄漏

//上面是先將節(jié)點b 的內(nèi)存地址同時給多個引用,然后在改變a.next的引用
//如果我們先改變a.next的應(yīng)用
a.next = x;
x.next = a.next
//這樣會產(chǎn)生的現(xiàn)象是奈搜,a.next 指向了x的地址悉盆,但是b的地址就無法找到了。
//然后x.next = a.next; 這時a.next 已經(jīng)是x的地址了馋吗。相等于x.next = x焕盟,造成內(nèi)存泄漏。
image.png

邊界判斷

//1宏粤、鏈表為空
/**
* 尾插入元素是脚翘,如果頭節(jié)點為null,上面的代碼就不適用了绍哎,需要對節(jié)點做一個判斷
**/
if(a == null){
    a = x; 
}else{
    Node temp = a;
    while(temp.next != null){
        temp = temp.next;
    }
    temp.next = x;
}
return a;
//刪除一個節(jié)點 a.next = a.next.next;但是如果刪除的是B節(jié)點来农,顯然也需要做判斷
//2、鏈表只有一個元素崇堰、兩個元素
//3沃于、頭節(jié)點和尾節(jié)點的處理

利用哨兵

// 上面在寫鏈表的處理的時候,插入新節(jié)點海诲,有兩種代碼邏輯繁莹,一種是頭節(jié)點null、一種是不為null特幔,邏輯是不一樣的
// 那么我們可以考慮另一種方式咨演,創(chuàng)建一個哨兵節(jié)點,這個節(jié)點什么元素都不存敬辣。只是簡化邊界判斷
 Node temp;
 Node head = new Node();
 head.next = a;
 temp = head;
 while (temp.next != null) {
    temp = temp.next;
 }
 temp.next = x;
 return head.next;
//這樣的代碼就可以處理雪标,那兩種情況,同理刪除元素

常見算法

  • 反轉(zhuǎn)鏈表
  • 合并有序鏈表
  • 刪除倒數(shù)第n個節(jié)點
  • 鏈表中間節(jié)點
  • 環(huán)狀鏈表檢測

反轉(zhuǎn)

// 輸入:1->2->3->4    輸出:4->3->2->1
// 思路: 1溉跃、迭代所有節(jié)點村刨,取當前節(jié)點    2、取當前節(jié)點的前一個節(jié)點  3撰茎、當前節(jié)點的next 指向前一個節(jié)點 
// 實現(xiàn):
Node reserver(Node curr) {
  Node pre = null; //作為全局變量嵌牺,保證下次遍歷時能訪問到,初始值null,因為第一次遍歷頭節(jié)點的下個節(jié)點是null
  while(curr != null){
    //頭節(jié)點(1)的前一個節(jié)點是null:curr.next = null; 
    //但是下一個節(jié)點(2)的前一個節(jié)點是頭節(jié)點(1) curr.next =?逆粹,所以我們需要將前一個節(jié)點保存起來募疮,下次迭代還能訪問
    curr.next = pre; // 當前節(jié)點curr的下個節(jié)點指向上一個 pre
    pre = curr; //將當前節(jié)點給pre預(yù)存起來,保證當前處理不會改變僻弹,下次迭代時使用,
                //如果沒有下次迭代阿浓,那么pre就是反轉(zhuǎn)后的頭節(jié)點
    curr = curr.next; 
  }
  return pre;
}
// 最后: 如果按照上面的代碼去執(zhí)行,會發(fā)現(xiàn)根本不是想要的結(jié)果蹋绽,1->null
// 原因: curr.next = pre 這行芭毙,curr的下個節(jié)點已經(jīng)被改變了(null),所以 curr = curr.next 其實等于curr = pre;
// 修改: 將curr原始下個節(jié)點預(yù)存,給一個變量 temp卸耘,然后curr = temp退敦;
Node reserver(Node node) {
    Node pre = null; //作為全局變量,保證下次遍歷時能訪問到蚣抗,初始值null侈百,因為第一次遍歷頭節(jié)點的下個節(jié)點是null
    while(node != null){
        //頭節(jié)點(1)的前一個節(jié)點是null:curr.next = null;
        //但是下一個節(jié)點(2)的前一個節(jié)點是頭節(jié)點(1) curr.next =?,所以我們需要將前一個節(jié)點保存起來翰铡,下次迭代還能訪問
        Node temp = node.next;
        node.next = pre; // 當前節(jié)點curr的下個節(jié)點指向上一個 pre
        pre = node; //將當前節(jié)點給pre預(yù)存起來钝域,保證當前處理不會改變,下次迭代時使用
        node = temp;
    }
    return pre;
}

排序

合并排序

//兩個有序鏈表合并一個有序鏈表
// 其中有一個為null,或者都為null
if(l1 == null) {
    return l2;
}
if(l2 == null) {
    return l1;
}
ListNode listNode = new ListNode(-1);
ListNode pre = listNode;
while(l1 != null && l2 != null){
    if(l1.val <= l2.val){
        pre.next = l1;
        l1 = l1.next;
    }else{
        pre.next = l2;
        l2 = l2.next;
    }
    pre = pre.next;
    return listNode.next;
}

刪除倒數(shù)第n個節(jié)點

//統(tǒng)計鏈表長度
ListNode removeNthFromEnd(ListNode head, int n) {
    int length  = 0;
    ListNode first = head;
    while (first != null) {
        length++;
        first = first.next;
    }
    //移除元素的前個節(jié)點的位置(length-=n)
    //例如:1->2->3->4->5,5個元素移除倒數(shù)第2個節(jié)點(4)锭魔,也就是3=5-2是4的前節(jié)點网梢。只要將3 ->5 就完成了刪除。
    length -= n;
    //定義一個帶有頭節(jié)點的鏈表
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    first = dummy;
    while (length > 0) {
        length--;
        first = first.next;
    }
    //如果length = 0赂毯,first = 3節(jié)點。所以將3.next = 3.next.next;
    first.next = first.next.next;
    return dummy.next;
}
//最后:邊界判斷 需要考慮拣宰,上面默認是參數(shù)是合法有效
//1党涕、鏈表為null,head=null,n=0 其中first.next =first.next.next 相當于 head = head.next,head=null巡社,所以會npt
//2、鏈表為一個元素/兩個元素 
//3、頭/尾節(jié)點
//4虑润、n值非法的情況

鏈表的中間節(jié)點

// 聽到中間立馬想到弥臼,快慢指針
public ListNode middleNode(ListNode head) {
  ListNode p = head; ListNode q = head;
  while(q!=null && q.next!=null){
      p = p.next;
      q = p.next.next;
  }
  return p;
}

鏈表中環(huán)檢測

public static boolean hasCycle(ListNode head) {
     // 如果2個節(jié)點以上,有循環(huán)鏈表朝群,現(xiàn)象:迭代一直會走下去燕耿。如何判斷有循環(huán),第一個想法某個節(jié)點迭代了兩次姜胖。
     // 1->2->3->4 4又指向了1 環(huán)就形成了誉帅,如果依次迭代,將之前元素放入集合,如果集合中存在蚜锨,則又環(huán)
     Set<ListNode> listNodes = new HashSet<>();
     while (head!=null){
         if (listNodes.contains(head)){
             return true;
         }
         listNodes.add(head);
         head = head.next;
     }
     return false;
}
//第二種 方式档插,就是使用快慢指針,想一下亚再,如果存在環(huán)的話郭膛。走的快的人肯定會跟走的慢的相遇,
//想一下操場上氛悬,有一個人速度是你的兩倍则剃,如果你跑完一圈,那個人肯定會跟你相遇圆雁。
//如果你跑到終點了忍级,還沒和你沒有相遇說明人家早到終點,停下來了伪朽。
public static boolean hasCycle(ListNode head) {
     // 判斷邊界 轴咱,1、head = null 2烈涮、head.next = null 3朴肺、head.next.next =null 4、頭尾節(jié)點坚洽。
     // 如果null 或者只有一個節(jié)點是不能形成循環(huán)節(jié)點(本身指向本身節(jié)點除外)
    if(head == null || head != null){
        return false;
    }
    // 是否是統(tǒng)一起點沒有關(guān)系戈稿,如果是環(huán)肯定會相遇
    ListNode p = head; ListNode q = head.next;
    while(q != null && q.next != null){
        if(p == q){
            return true;
        }
        p = p.next;
        q = q.next.next;
    }
    return false;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市讶舰,隨后出現(xiàn)的幾起案子鞍盗,更是在濱河造成了極大的恐慌,老刑警劉巖跳昼,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件般甲,死亡現(xiàn)場離奇詭異,居然都是意外死亡鹅颊,警方通過查閱死者的電腦和手機敷存,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堪伍,“玉大人锚烦,你說我怎么就攤上這事〉酃停” “怎么了涮俄?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摊求。 經(jīng)常有香客問我禽拔,道長刘离,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任睹栖,我火速辦了婚禮硫惕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘野来。我一直安慰自己恼除,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布曼氛。 她就那樣靜靜地躺著豁辉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舀患。 梳的紋絲不亂的頭發(fā)上徽级,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音聊浅,去河邊找鬼餐抢。 笑死,一個胖子當著我的面吹牛低匙,可吹牛的內(nèi)容都是我干的旷痕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼顽冶,長吁一口氣:“原來是場噩夢啊……” “哼欺抗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起强重,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绞呈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后间景,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體报强,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年拱燃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片力惯。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡碗誉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出父晶,到底是詐尸還是另有隱情哮缺,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布甲喝,位于F島的核電站尝苇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜糠溜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一淳玩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧非竿,春花似錦蜕着、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锤悄,卻和暖如春韧骗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背零聚。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工袍暴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人握牧。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓容诬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沿腰。 傳聞我的和親對象是個殘疾皇子览徒,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容