数据结构论坛

首页 » 分类 » 分类 » 数据结构与算法gtgt双向链
TUhjnbcbe - 2024/2/27 15:46:00

一、双向链表

使用带head头的双向链表实现-水浒英雄排行榜管理单向链表的缺点分析:

)单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。

2)单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,所以前面我们单链表删除节点时,总是找到temp,temp时待删除节点的前一个节点(认真体会)。

分析双向链表的遍历,添加,修改,删除的操作思路

.遍历和单链表一样只是可以向前,也可以向后查找

2.添加(默认添加到双向链表的最后)

)先找到双向链表的最后这个节点

2)temp.next=newHeroNode

)newHeroNode.p=temp

.修改思路和原理与单向链表一样

4.删除

)因为时双向链表,因此,我们可以实现自我删除某个节点

2)直接找到要删除的这个节点,比如temp

)temp.p.next=temp.next

4)temp.next.p=temp.p

publicclassDoubleLinkedListDemo{  publicstaticvoidmain(String[]args){    //测试    System.out.println("双向链表的测试");    //先创建节点    HeroNode2hero=newHeroNode2(,"宋江","及时雨");    HeroNode2hero2=newHeroNode2(2,"卢俊义","玉麒麟");    HeroNode2hero=newHeroNode2(,"吴用","智多星");    HeroNode2hero4=newHeroNode2(4,"林冲","豹子头");    //创建一个双向链表    DoubleLinkedListdoubleLinkedList=newDoubleLinkedList();    //加入    doubleLinkedList.add(hero);    doubleLinkedList.add(hero2);    doubleLinkedList.add(hero);    doubleLinkedList.add(hero4);    doubleLinkedList.list();    //修改    HeroNode2newHeroNode=newHeroNode2(4,"公孙胜","入云龙");    doubleLinkedList.update(newHeroNode);    System.out.println("修改后的链表情况");    doubleLinkedList.list();    //删除    doubleLinkedList.del();    System.out.println("删除后的链表情况~~");    doubleLinkedList.list();  }}//创建一个双向链表的类classDoubleLinkedList{  //先初始化一个头节点,头节点不要动,不存放具体的数据  privateHeroNode2head=newHeroNode2(0,"","");  //返回头节点  publicHeroNode2getHead(){    turnhead;  }  //显示链表[遍历]  publicvoidlist(){    //判断链表是否为空    if(head.next==null){      System.out.println("链表为空");      turn;    }    //因为头节点,不能动,因此我们需要一个辅助变量来遍历    HeroNode2temp=head.next;    while(true){      //判断是否到链表最后      if(temp==null){        bak;      }      //输出节点的信息      System.out.println(temp);      //将temp后移,一定小心      temp=temp.next;    }  }  //添加一个节点到双向链表的最后  publicvoidadd(HeroNode2heroNode){    //因为head节点不能动,因此我们需要一个辅助变量temp    HeroNode2temp=head;    //遍历链表,找到最后    while(true){      //找到链表的最后      if(temp.next==null){        bak;      }      //如果没有找到最后,将temp后移      temp=temp.next;    }    //当退出while循环时,temp就指向了链表的最后    //形成一个双向链表    temp.next=heroNode;    heroNode.p=temp;  }  //修改一个节点的内容,双向链表的节点内容修改和单向链表一样  //只是节点类型改成HeroNode2  publicvoidupdate(HeroNode2newHeroNode){    //判断是否空    if(head.next==null){      System.out.println("链表为空~~");      turn;    }    //找到需要修改的节点,根据no编号    //定义一个辅助变量    HeroNode2temp=head.next;    booleanflag=false;//表示是否找到该节点    while(true){      if(temp==null){        bak;//已经遍历完链表      }      if(temp.no==newHeroNode.no){        //找到        flag=true;        bak;      }      temp=temp.next;    }    //根据flag判断是否找到要修改的节点    if(flag){      temp.name=newHeroNode.name;      temp.nickname=newHeroNode.nickname;    }else{//没有找到      System.out.printf("没有找到编号%d的节点,不能修改\n",newHeroNode.no);    }  }  //从双向链表中删除一个节点  //说明  //.对于双向链表,我们可以直接找到要删除的这个节点  //2.找到后,自我删除即可  publicvoiddel(intno){    //判断当前链表是否为空    if(head.next==null){//空链表      System.out.println("链表为空,无法删除");      turn;    }    HeroNode2temp=head.next;//辅助变量(指针),指向第一个节点(与单向链表不同)    booleanflag=false;//标志是否找到待删除节点    while(true){      if(temp.next==null){//已经到链表的最后节点的next        bak;      }      if(temp.next.no==no){        //找到的待删除节点的前一个节点temp        flag=true;        bak;      }      temp=temp.next;//temp后移,遍历    }    //判断flag    if(flag){//找到      //可以删除      temp.p.next=temp.next;      //如果是最后一个节点,就不需要执行下面的这句话,否则出现空指针      if(temp.next!=null){        temp.next.p=temp.p;      }    }else{      System.out.printf("要删除的%d节点不存在\n",no);    }  }}//定义HeroNode2,每个HeroNode对象就是一个节点classHeroNode2{  publicintno;  publicStringname;  publicStringnickname;  publicHeroNode2next;//指向下一个节点,默认为null  publicHeroNode2p;//指向前一个节点,默认为null  //构造器  publicHeroNode2(intno,Stringname,Stringnickname){    this.no=no;    this.name=name;    this.nickname=nickname;  }  //为了显示方便,我们重写toString  

Override  publicStringtoString(){    turn"HeroNode2[no="+no+",name="+name+",nickname="+nickname+"]";  }}

二、环形链表及其应用:约瑟夫问题

环形链表图示

构建一个单向的环形链表思路

.先创建第一个节点,让first指向该节点,并形成环形

2.后面当我们每创建一个新的节点,就把该节点加入到已有的环形链表中即可。

遍历环形链表

.先让一个辅助指针(变量)curBoy,指向first节点

2.然后通过一个while循环遍历该环形链表即可cur.Boy.next==first结束

约瑟夫问题

.创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点。

2.小孩报数前,先让first和helper移动k-次(移动到报数的小孩)

.当小孩报数时,让first和helper指针同时的移动m-次

4.这时就可以将first指向的小孩节点出圈

first=first.next

helper.next=first

原来first指向的节点就没有任何引用,就会被回收

publicclassJosepfu{  publicstaticvoidmain(String[]args){    //测试看看构建环形链表,和遍历是否ok    CircleSingleLinkedListcircleSingleLinkedList=newCircleSingleLinkedList();    circleSingleLinkedList.addBoy(5);//加入5个小孩节点    circleSingleLinkedList.showBoy();    //测试小孩出圈是否正确    circleSingleLinkedList.countBoy(,2,5);//2-4--5-  }}//创建一个环形的单向链表classCircleSingleLinkedList{  //创建一个first节点,当前没有编号  privateBoyfirst=null;  //添加小孩节点,构建一个环形的链表  publicvoidaddBoy(intnums){    //nums做一个数据校验    if(nums){      System.out.println("nums的值不正确");      turn;    }    BoycurBoy=null;//辅助指针,帮助构建环形链表    //使用for来创建环形链表    for(inti=;i=nums;i++){      //根据编号,创建小孩节点      Boyboy=newBoy(i);      //如果是第一个小孩      if(i==){        first=boy;        first.setNext(first);//构成环(暂时是一个节点的环)        curBoy=first;//让curBoy指向第一个小孩      }else{//这块的操作看不懂,可以回去看一下当时老师视频里的流程图,特别好理解!!!!!!!!!!        curBoy.setNext(boy);        boy.setNext(first);        curBoy=boy;      }    }  }  //遍历当前的环形链表  publicvoidshowBoy(){    //判断链表是否为空    if(first==null){      System.out.println("没有任何小孩~~");      turn;    }    //因为first不能动,因此我们仍然使用一个辅助指针完成遍历    BoycurBoy=first;    while(true){      System.out.printf("小孩的编号%d\n",curBoy.getNo());      if(curBoy.getNext()==first){//说明已经遍历完毕        bak;      }      curBoy=curBoy.getNext();//curBoy后移    }  }  //根据用户的输入,计算出小孩出圈的顺序  /**  *

paramstartNo表示从第几个小孩开始数数  *

paramcountNum表示数几下  *

paramnums表示最初有多少小孩在圈中  */  publicvoidcountBoy(intstartNo,intcountNum,intnums){    //先对数据进行校验    if(first==null

startNo

startNonums){      System.out.println("参数输入有误,请重新输入");      turn;    }    //创建一个辅助指针,帮助完成小孩出圈    Boyhelper=first;    //需要创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点    while(true){      if(helper.getNext()==first){//说明helper指向最后小孩节点        bak;      }      helper=helper.getNext();    }    //小孩报数前,先让first和helper移动k-次    for(intj=0;jstartNo-;j++){      first=first.getNext();      helper=helper.getNext();    }    //当小孩报数时,让first和helper指针同时的移动m-次,然后出圈    //这里是一个循环操作,直到圈中只有一个节点    while(true){      if(helper==first){//说明圈中只有一个节点        bak;      }      //让first和helper指针同时的移动countNum-      for(intj=0;jcountNum-;j++){        first=first.getNext();        helper=helper.getNext();      }      //这时first指向的节点,就是要出圈的小孩节点      System.out.printf("小孩%d出圈\n",first.getNo());      //这时将first指向的小孩节点出圈      first=first.getNext();      helper.setNext(first);    }    System.out.printf("最后留在圈中的小孩编号%d\n",first.getNo());  }}//创建一个Boy类,表示一个节点classBoy{  privateintno;//编号  privateBoynext;//指向下一个节点,默认null  publicBoy(intno){    this.no=no;  }  publicintgetNo(){    turnno;  }  publicvoidsetNo(intno){    this.no=no;  }  publicBoygetNext(){    turnnext;  }  publicvoidsetNext(Boynext){    this.next=next;  }}

,

1
查看完整版本: 数据结构与算法gtgt双向链