java数据结构—单链表的实现原理

    xiaoxiao2021-03-25  158

    再次学习数据额结构,看到前面的单链表,感觉里面的思路很不错,自己动手写代码尝试一下,果然一动手就发现自己并没有完全理解。这里主要记录我花了很长时间才理解的地方,不去考虑增删改查,我觉得这些功能在很多地方都实现过,不是重点,也有很多资料可查。

    单链表的原理网上很多,这里也不解释,先上代码。

    这是一个节点类,用于记录节点内容和对象引用(地址),其中不包含一些基本功能,只写了必要的函数。

    public class Node { Object data;//节点包含的具体内容 Node next;//节点的引用,用于指向下一个节点 Node (Node i){//构造函数1,用于初始化头结点 next=i; } Node (Object i,Node j){//构造函数2,用于其他节点。 data=i; next=j; } public void setnext(Node nextval){//这个函数作用是把其他节点的引用指向这个节点。 next=nextval; } public Object getelement(){//获取节点的内容 return data; } }

    这是我第一次做的测试函数

    public class main_Node { public static void main(String[] args) { Node head;//头结点 Node current;//表示当前节点 current=head=new Node(null);//初始化为空 current.setnext(new Node(1,head.next));//添加一个节点 current.setnext(new Node(2,current.next));//添加两个节点 current.setnext(new Node(3,current.next));//添加三个节点 current=head.next;//让current表示head的下一个节点,也就是第一个节点。 int a=(int)current.getelement();//获取该节点的内容 System.out.println(a);//打印 } }

    结果为3,这里就有问题了,我命名让他指向第一个节点,应该是1才对呀,怎么会变成第三个呢?debug的结果是这样的::

    可以看出来,这里的head直接指向的是3,刚好反过来了,所以刚才得出的值是3。

    看似很简单的问题,我还是花了很长时间才发现问题。其实原因是这样添加节点时,current所指向的节点并没有变,还是在头结点位置,添加节点并不能使current表示的节点后移,所以解决办法是在每次添加了新的节点后把current往后移一下,让它指向新添加的节点,这样才能在新添加的节点后面再添加节点。代码如下:

    public class main_test1 { public static void main(String[] args) { test1 head; test1 current; current=head=new test1(null);//这里也表示current指向头结点 current.setnext(new test1(1,head.next)); current=current.next;//使current指向下一个节点 current.setnext(new test1(2,current.next)); current=current.next; current.setnext(new test1(3,current.next)); current=current.next; current=head.next; int a=(int)current.getelement(); System.out.println(a); } }

    结果为1。这次正确了,再看debug图: 看,这下顺序就对了。 这里主要的问题是,没有想到添加新节点后想在其后面继续添加,是要先移动当前current所指向的节点的位置的。感觉以后这个思路会给我帮助,记录下来方便以后复习。

    补充:一个单链表的优秀技巧贴:http://wuchong.me/blog/2014/03/25/interview-link-questions/

    对这篇博客的一些地方做一些补充,记录自己思考的结果: 第八点:链表有环,如何判断相交: 我自己想了另一种方法,应该比博客中的效率高一点,其实也是借鉴了上面的思想,设置三个节点,其中一快一慢(和前面的一个意思,快的前进两格)指向head1,另一个快指向head2。这里思路是:如果两个有环链表相交(这里不考虑两个无环链表相交的情况),那么当前面设置的三个节点都进入环中的时候,就可以利用head2_fast去追赶head1_low,这里最关键的地方是考虑什么时候停止追赶,因为环内是可以无限循环的。仔细琢磨第5点的原理就能找到关键点了,看代码理解吧:

    public boolean isIntersect(Node head1,Node head2){ //设置三个节点 Node head1_low=head1; Node head1_fast=head1; Node head2_fast=head2; while(head1_fast!=null && head1_fast.next!=null && head2_fast!=null && head2_fast.next!=null){//如果有无环链表直接判断不相交 head1_low=head1_low.next; head1_fast=head1_fast.next.next; head2_fast=head2_fast.next.next; if(head1_low==head2_fast)//过程中如果运气好,可以直接得到相交 return true; else if(head1_low==head1_fast){//核心,也就是找到慢指针和快指针相交的点后就进行head2_fast追赶head1_low do{//这里有个问题,如果head2除了环之外的部分特别长,那么这里是不成立的,就需要设立第四个节点,后面给出 head1_low=head1_low.next; head2_fast=head2_fast.next.next; if(head1_low==head2_fast) return true; }while(head1_low.next!=head1_fast);//如果相交,head1_low跑一圈内必然能够相等。 return false; } } return false; }

    给出上面的修正代码,其实就是要确认开始追赶时head2_fast已经进入环内,不然会出现head1_low跑完一圈head2_fast还未进环的情况,同样通过给head2_fast也找个相交点来判断是否进环:

    public boolean isIntersect(Node head1,Node head2){ Node head1_low=head1; Node head1_fast=head1; Node head2_low=head2; Node head2_fast=head2; while(head1_fast!=null && head1_fast.next!=null && head2_fast!=null && head2_fast.next!=null){ head1_low=head1_low.next; head1_fast=head1_fast.next.next; head2_low=head1_low.next; head2_fast=head2_fast.next.next; if(head1_low==head2_fast) return true; else if(head1_low==head1_fast){ while(head2_low!=head2_fast){ head2_low=head1_low.next; head2_fast=head2_fast.next.next; if(head2_fast!=null && head2_fast.next!=null) return false; } do{ head1_low=head1_low.next; head2_fast=head2_fast.next.next; if(head1_low==head2_fast) return true; }while(head1_low.next!=head1_fast); return false; } else if(head2_low==head2_fast){ while(head1_low!=head1_fast){ head1_low=head1_low.next; head1_fast=head1_fast.next.next; if(head1_fast!=null && head1_fast.next!=null) return false; } do{ head1_low=head1_low.next; head2_fast=head2_fast.next.next; if(head1_low==head2_fast) return true; }while(head1_low.next!=head1_fast); return false; } } return false; }

    其实变化不大,看一看就明白,不过这样一来和原方法比较优势就不明显了。总之提供一种思路吧(以上方法没有经过检验,可能有瑕疵)。

    第九点:两链表相交的第一个公共节点: 博主没有写这种方法,也许是他觉得没必要,因为所有东西博客上都提到了,有心人应该是自己能推理出来的: 把一个链表接到另一个链表的尾部,形成环,再用判断环的入口,就ok了。是不是所有东西博客里都有写到?

    转载请注明原文地址: https://ju.6miu.com/read-1560.html

    最新回复(0)