天天看點

新星計劃Day6【資料結構與算法】 連結清單Part2

新星計劃Day6【資料結構與算法】 連結清單Part2

👩‍💻部落格首頁:京與舊鋪的部落格首頁

✨歡迎關注🖱點贊🎀收藏⭐留言✒

🔮本文由京與舊鋪原創

😘系列專欄:java學習

💻首發時間:🎞2022年4月30日🎠

🎨你做三四月的事,八九月就會有答案,一起加油吧

🀄如果覺得部落客的文章還不錯的話,請三連支援一下部落客哦

🎧最後的話,作者是一個新人,在很多方面還做的不好,歡迎大佬指正,一起學習哦,沖沖沖

💬推薦一款模拟面試、刷題神器👉​​​點選進入網站​​

新星計劃Day6【資料結構與算法】 連結清單Part2

🛒導航小助手🎪

文章目錄

  • ​​新星計劃Day6【資料結構與算法】 連結清單Part2​​
  • ​​🛒導航小助手🎪​​
  • ​​@[toc]​​
  • ​​🏳‍🌈021 單連結清單新浪面試題​​
  • ​​💺022 單連結清單騰訊面試題​​
  • ​​🚌023 單連結清單百度面試題​​
  • ​​🌍024 雙向連結清單增删改查分析圖解​​
  • ​​🦽025 雙向連結清單增删改查代碼實作​​
  • ​​🚡026 雙向連結清單功能測試和小結​​
  • ​​🚞027 環形連結清單介紹和約瑟夫問題​​
  • ​​🚂028 約瑟夫問題分析圖解和實作(1)​​
  • ​​💌029 約瑟夫問題分析圖解和實作(2)​​

🏳‍🌈021 單連結清單新浪面試題

查找單連結清單中的倒數第k個節點(新浪面試題)

思路

1.編寫一個方法,接收head節點,同時接收一個index

2.index表示是倒數第index個節點

3.先把連結清單從頭到尾周遊一下,得到這個連結清單的總的長度getLength

4.得到size後,我們從連結清單的第一個開始周遊(size-index)個,就可以得到了

5.如果找到了,則傳回該節點,否則傳回null

public static HeroNode findLastIndexNode(HeroNode head,int index){
    //判斷如果連結清單為空,傳回null
    if(head.next==null){
        return null;//沒有找到
    }
    //第一次周遊得到連結清單的長度(節點個數)
    int size=getLength(head);
    //第二次周遊size-index位置,就是我們倒數的第K個節點
    //先做一個index的校驗
    if(index<=0||index>size){
        return null;
    }
    //定義一個輔助變量,for循環定位到倒數的index
    HeroNode cur=head.next;
    for(int i=0;i<size-index;i++){
        cur=cur.next;
    }
    return cur;
}      

💺022 單連結清單騰訊面試題

将單連結清單進行反轉

思路:1.先定義一個節點reverseHead=new HeroNode();

2.從頭到尾周遊原來的連結清單,每周遊一個節點,就将其取出,并放在新的連結清單的最前端

3.原來的連結清單的head.next=reverseHead.next

public static void reverseList(HeroNode head){
    //如果目前連結清單為空,或者隻有一個節點,無需反轉,直接傳回
    if(head.next==null||head.next.next==null){
        return;
    }
    //定義一個輔助的指針(變量),幫助我們周遊原來的連結清單
    HeroNode cur=head.next;
    HeroNode next=null;//指向目前節點【cur】的下一個節點
    HeroNode reverseHead=new HeroNode(0,"","");
    //從頭到尾周遊原來的連結清單,每周遊一個節點,就将其取出,并放在新的連結清單的最前端
    //動腦筋
    while(cur!=null){
        next=cur.next;//先暫時儲存目前節點的下一個節點,因為後面需要使用
        cur.next=reverseHead.next;//将cur的下一個節點指向新的連結清單的頭節點
        reverseHead.next=cur;//将cur連接配接到新的連結清單
        cur=next;//讓cur後移
    }
    //将head.next指向reverseHead.next,實作單連結清單的反轉
    head.next=reverseHead.next;
}      

🚌023 單連結清單百度面試題

從尾到頭列印單連結清單

思路:

1.上面的題的要求就是逆序列印單連結清單

2.方式一:先将單連結清單進行一個反轉操作,然後再周遊即可,這樣做的問題是會破壞原來的單連結清單的結構,不建議這樣做

3.方式二:可以利用棧這個資料結構,将各個節點壓入到棧中,利用棧的先進後出的特點就實作了這個逆序列印的效果

舉例示範棧的使用

import java.util.Stack;
public class TestStack{
    public static void main(String[] args){
        Stack<String> stack=new Stack();
        //入棧
        stack.add("jack");
        stack.add("tom");
        stack.add("smith");
        //出棧
        //smith,tom,jack
        while(stack.size()>0){
            System.out.println(stack.pop());//pop就是将棧頂的資料取出
        }
    }
}      

方式二:可以利用棧這個資料結構,将各個節點壓入到棧中,利用棧的先進後出的特點就實作了這個逆序列印的效果

public static void reversePrint(HeroNode head){
    if(head.next==null){
        return;//空連結清單,不能列印
    }
    //建立要給一個棧,将各個節點壓入棧
    Stack<HeroNode> stack=new Stack<HeroNode>();
    HeroNode cur=head.next;
    //将連結清單的所有節點壓入棧中
    while(cur!=null){
        stack.push(cur);
        cur=cur.next;//cur後移,這樣就可以壓入下一個節點
    }
    //将棧中的節點進行列印,pop出棧
    while(stack.size()>0){
        System.out.println(stack.pop());//stack的特點是先進後出
    }
}      

🌍024 雙向連結清單增删改查分析圖解

單連結清單的缺點

1.單向連結清單的查詢節點隻能是一個方向

2.單向連結清單不能實作自我删除,隻能通過找到前一個節點來删除

雙向連結清單分析

新星計劃Day6【資料結構與算法】 連結清單Part2

pre:指向前一個節點

分析雙向連結清單的周遊,添加,修改,删除的操作思路—代碼實作

1)周遊的方式和單連結清單一樣,隻是可以向前查找,也可以向後查找

2)添加(預設添加到雙向連結清單的最後)

(1)先找到雙向連結清單的最後這個節點

(2)temp.next=newHeroNode

(3)newHeroNode.pre=temp;

(4)修改思路和原來的單向連結清單一樣

(5)删除

(1)因為是雙向連結清單,是以,我們可以實作自我删除某個節點

(2)直接找到要删除的這個節點,比如temp

(3)temp.pre.next=temp.next

(4)temp.next.pre=temp.pre;

🦽025 雙向連結清單增删改查代碼實作

//建立一個雙向連結清單的類
class DoubleLinkedList{
    //先初始化一個頭節點,頭節點不要動,不存放具體的資料
    private HreoNode2 head=new HeroNode2(0,"","");
    //傳回頭節點
    public HeroNode2 getHead(){
        return head;
    }
    //周遊雙向連結清單的方法
    //顯示連結清單【周遊】
    public void list(){
        //判斷連結清單是否為空
        if(head.next==null){
            System.out.println("連結清單為空");
            return;
        }
        //因為頭節點,不能動,是以我們需要一個輔助變量來周遊
        HeroNode temp=head.next;
        while(true){
            //判斷是否到連結清單最後
            if(temp==null){
                break;
            }
            //輸出節點的資訊
            System.out.println(temp);
            //将temp後移,一定小心
            temp=temp.next;
        }
    }
    //添加一個節點到雙向連結清單的最後
    public void add(HeroNode2 heroNode){
        //因為head節點不能動,是以我們需要一個輔助周遊temp
        HeroNode2 temp=head;
        //周遊連結清單,找到最後
        while(true){
            if(temp.next==null){
                break;
            }
            //如果沒有找到最後,将temp後移
            temp=temp.next;
        }
        //當退出while循環時,temp就指向了連結清單的最後
        //形成一個雙向連結清單
        temp.next=heroNode;
        heroNode.pre=temp;
    }
    //修改一個節點的内容,可以看到雙向連結清單的節點内容修改和單向連結清單一樣
    //隻是節點的類型改成了HeroNode2
    public void update(HeroNode2 newHeroNode){
        //判斷是否為空
        if(head.next==null){
            System.out.println("連結清單為空");
            return;
        }
        //找到需要修改的節點,根據no編寫
        //定義一個輔助變量
        HeroNode2 temp=head.next();
        boolean flag=false;//表示是否找到節點
        while(true){
            if(temp==null){//已經周遊完連結清單
                break;
            }
            if(temp.no==newHeroNode.no){
                //找到
                flag=true;
                break;
            }
            temp=temp.next;
        }
        //根據flag判斷是否找到要修改的節點
        if(flag){
            temp.name=newHreoNode.name;
             temp.nickname=newHreoNode.nickname;
        }else{//沒有找到
             System.out.printt("沒有找到編号%d的節點,不能修改\n",newHeroNode.no);
        }
    }
    //從雙向連結清單中删除一個節點
    //1.對于雙向連結清單,我們可以直接找到要删除的這個節點
    //2.找到後,自我删除即可
    public void del(int no){
        //判斷目前連結清單是否為空
        if(head.next==null){//空連結清單
            System.out.println("連結清單為空,無法删除");
            return;
        }
        HeroNode2 temp=head.next;//輔助變量(指針)
        boolean flag=false;//标志是否找到待删除節點的
        while(true){
            if(temp==null){//已經到連結清單的最後
                break;
            }
            if(temp.no==no){
                //找到了待删除節點的前一個節點temp
                flag=true;
                break;
            }
            temp=temp.next;//temp後移,周遊
        }
        //判斷flag
        if(flag){//找到
            //可以删除
            temp.pre.next=temp.next;
            //這裡我們的代碼有問題  
            //如果是最後一個節點,就不需要執行下面這句話,否則會出現空指針異常
            if(temp.next!=null){
                temp.next.pre=temp.pre;
            }
            
        }else{
            System.out.printf("要删除的%d節點不存在\n",no);
        }
    }
}
//定義HeroNode2,每個HeroNode對象就是一個節點
class HeroNode2{
    public int no;
    public String name;
    public String nickname;
    public HeroNode next;//執行下一個節點,預設為null
    public HeroNode pre;//指向前一個節點,預設為null
    //構造器
    public HeroNode(int hNo,String name,String nickname){
        this.no=no;
        this.name=name;
        this.nickname=nickname;
    }
    //為了顯示友善,我們重寫一下toString
    @Override
    public String toString(){
        return "HeroNode [no="+no+",name="+name+",nickname="+nickname+"]";
    }
    
}      

🚡026 雙向連結清單功能測試和小結

public class DoubleLinkedListDemo{
    public static void main(String[] args){
        //測試
        System.out.println("雙向連結清單的測試");
        //先建立節點
        HeroNode2 her1 = new HeroNode2(1, "宋江", "及時雨");
        HeroNode2 her2 = new HeroNode2(2, "盧俊義", "玉麒麟");
        HeroNode2 her3 = new HeroNode2(3, "吳用", "智多星");
        HeroNode2 her4 = new HeroNode2(4, "林沖", "豹子頭");
        //建立一個雙向連結清單
        DoubleLinkedList doubleLinkedList=new DoubleLinkedList;
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);
        
        doubleLinkedList.list();
        //修改
        HeroNode2 newHeroNode = new HeroNode2(4, "公孫勝", "入雲龍");
        doubleLinkedList.update(newHeroNode);
        System.out.println("修改後的連結清單情況");
        doubleLinkedList.list();
        //删除
        doubleLinkedList.del(3);
        System.out.println("删除後的連結清單情況");
        doubleLinkedList.list();
    }
}      

🚞027 環形連結清單介紹和約瑟夫問題

Josephu(約瑟夫、約瑟夫環) 問題 Josephu 問題為:設編号為 1,2,… n 的 n 個人圍坐一圈,約定編号為 k(1<=k<=n)的人從 1 開始報數,數 到 m 的那個人出列,它的下一位又從 1 開始報數,數到 m 的那個人又出列,依次類推,直到所有人出列為止,由 此産生一個出隊編号的序列。

提示:用一個不帶頭結點的循環連結清單來處理 Josephu 問題:先構成一個有 n 個結點的單循環連結清單,然後由 k 結 點起從 1 開始計數,計到 m 時,對應結點從連結清單中删除,然後再從被删除結點的下一個結點又從 1 開始計數,直 到最後一個結點從連結清單中删除算法結束

🚂028 約瑟夫問題分析圖解和實作(1)

建構一個單向的環形連結清單思路

1.先建立第一個節點,讓first指向該節點,并形成環形

2.後面當我們每建立一個新的節點,就把該節點,加入到已有的環形連結清單中即可

周遊環形連結清單

1.先讓一個輔助指針(變量)指向first節點

2.然後通過一個while循環周遊該環形連結清單即可 cueBoy.next==first結束

public class Josepfu{
    public static void main(String[] args){
        //測試一把看看建構環形連結清單,和周遊是否ok
        CircleSingleLinkedList circleSingleLinkedList=new CircleSingleLinkedList;
        circleSingleLinkedList.addBoy(5);//加入五個小孩節點
        circleSingleLinkedList.showBoy();
    }
}
//建立一個環形的單向連結清單
class CircleSingleLinkedList{
    //建立一個first節點,目前沒有編号
    private Boy first=new Boy(-1);
    //添加小孩的節點,建構成一個環形連結清單
    public void addBoy(int nums){
        //nums做一個資料校驗
        if(nums<1){
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy=null;//輔助指針,幫助建構環形連結清單
        //使用for循環來建立我們的環形連結清單
        for(int i=1;i<=nums;i++){
            //根據編号建立小孩節點
            Boy boy=new Boy(i);
            //如果是第一個小孩
            if(i==1){
                first=boy;
                first.setNext(first);//構成環
                curBoy=first;//讓curBoy指向第一個小孩
            }else{
                curBoy.setNext(boy);//
                boy.setNext(first);//
                curBoy=boy;
            }
        }
    }
    //周遊目前環形連結清單
    public void showBoy(){
        //判斷連結清單是否為空
        if(first==null){
            System.out.println("沒有任何小孩");
            return;
        }
        //因為first不能動,是以我們仍然使用一個輔助指針完成周遊
        Boy curBoy=first;
        while(true){
            System.out.printf("小孩的編号%d \n",curBoy.getNo());
            if(curBoy.getNext()==first){//說明已經周遊完畢
                break;
            }
            curBoy=curBoy.getNext();//curBoy後移
        }
    }
}
//建立一個Boy類,表示一個節點
class Boy{
    private int no;//編号
    private Boy next;//指向下一個節點,預設null
    public Boy(int no){
        this.no=no;
    }
    public int getNo(){
        return no;
    }
    public void setNo(int  no){
        this.no=no;
    }
    public Boy getNext(){
        return next;
    }
    public void setNext(Boy next){
        this.next=next;
    }
}      

💌029 約瑟夫問題分析圖解和實作(2)

根據使用者的輸入,生成一個小孩出圈的順序

n=5,即有5個人

k=1,從第一個人開始報數

m=2,數2下

1.需要建立一個輔助指針(變量)helper,事先應該指向環形連結清單的最後這個節點

補充:

小孩報數前,先讓first和helper移動k-1次

2.當小孩報數時,讓first和helper這個指針同時的移動m-1次

3.這時都可以将first指向的小孩節點出圈

first=first.next

helper.next=first;

原來first指向的節點就沒有任何引用了,就會被回收

//startNo  表示從第幾個小孩開始數數
//countNum  表示數幾下
//nums 表示最初有多少小孩在圈中
public void countBoy(int startNo,int countNum,int nums){
    //先對資料進行校驗
    if(first==null||starNo<1||startNo>nums){
        System.out.println("參數輸入有誤,請重新輸入");
        return;
    }
    //建立要給輔助指針,幫助完成小孩出圈
    Boy helper=first;
    //需要建立一個輔助指針(變量)helper,事先應該指向環形連結清單的最後這個節點
    while(true){
        if(helper.getNext()==first){//說明helper指向最後小孩節點
            break;
        }
        helper=helper.getNext();
    }
    //小孩報數前,先讓first和helper移動k-1次
    for(int j=0;j<startNo-1;j++){
        first=first.getNext();
        helper=helper.getNext();
    }
    //當小孩報數時,讓first和helper這個指針同時的移動m-1次,然後出圈
    //這裡是一個循環操作,直到圈中隻有一個節點
    while(true){
        if(helper==first){  //說明圈中隻有一個節點
            break;
        }
        //讓first和helper指針同時移動countnum-1次
        for(int j=0;j<countNum-1;j++){
            first=first.getNext();
        helper=helper.getNext();
        }
        //這時first指向的節點,就是要出圈的小孩節點
        first=first.getNext();
        helper.setNext(first);
        
    }
    System.out.printf("最後留在圈中的小孩編号為%d\n",first.getNo());
}      

下期預告:力扣每日一練之數組中篇Day2

繼續閱讀