数据结构与算法-java-单链表

首先搞懂什么是链表

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的,链表通过一个指向下一个元素地址的引用将链表中的元素串起来。

那么再对比一下数组

数组的内存是连续分配的,并且是静态分配的,即在使用数组之前需要分配固定大小的空间。可以通过索引直接得到数组中的而元素,即获取数组中元素的时间复杂度为O(1)。

链表分类

链表分为单向链表(Singly linked lis)、双向链表(Doubly linked list)、循环链表(Circular Linked list)。

它在内存里面的实际结构如下,而我们大多探究的是链表的逻辑结构

逻辑和实际的区别就是他的next有可能在180而它本身在110这个地址如下所示。而为了方便我们依然把它们挨着画出并连接

 

 

单向链表(Singly linked lis)

单向链表是最简单的链表形式。我们将链表中最基本的数据称为节点(node),每一个节点包含了数据块和指向下一个节点的指针

逻辑结构如下(带头结点)

 

 

 

 那么我们用一个实例看看单链表

 

 

 

 下面来看代码

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        //测试
        //先创建节点
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
        //创建要给的链表
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        //加入
        singleLinkedList.add(hero1);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);
        singleLinkedList.add(hero4);
        //显示
        singleLinkedList.show();

    }

}


class SingleLinkedList{
    //先初始化的头结点,不存放数据
    private HeroNode head = new HeroNode(0,"","");


    //add
    //添加的第一步是找到这个链表的最后那个节点,将最后节点的next指向新的即可
    public void add(HeroNode heroNode)
    {
        //因为head头结点不能动,所以需要辅助temp
        HeroNode temp = head;//复制过去,既不破坏head又能遍历
        //遍历链表,找到最后
        while (true) {
            //找到链表最后
            if (temp.next == null) {
                break;
            }
            //如果没有找到最后就后移
            temp=temp.next;
            //继续循环while到if去直到找到最后,跳出循环即break所以出来时候的temp一定是最后节点

        }
        //将最后节点的next指向add的
        temp.next = heroNode ;
    }

    //show
    public void show(){
        //判断是否链表空
        if(head.next == null) {
            System.out.println("链表为空");
            return;
        }
        else {
            HeroNode temp = head.next;
            while (true)
            {
                //到最后了,老规矩
                if(temp.next ==null)
                {
                    break;
                }
                //没到最后就输出
                System.out.println(temp); //因为我们呢重写了tostring方法,直接就可以打印chu
                //后移,不然一直原地踏步
                temp = temp.next;
            }
        }
    }
}



//先定义一个HeroNode,每一个HeroNode就是一个节点,就是代替c++中的struct
class HeroNode{
    public int no;
    public String name;
    public String nickname;
    public HeroNode next; //指向下一节点
    //构造器
    public HeroNode(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }
    //为了显示方便
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                ", next=" + next +
                '}';
    }
}

但是结果还差了一点,很是奇怪。如下

HeroNode{no=1, name='宋江', nickname='及时雨', next=HeroNode{no=2, name='卢俊义', nickname='玉麒麟', next=HeroNode{no=3, name='吴用', nickname='智多星', next=HeroNode{no=4, name='林冲', nickname='豹子头', next=null}}}}
HeroNode{no=2, name='卢俊义', nickname='玉麒麟', next=HeroNode{no=3, name='吴用', nickname='智多星', next=HeroNode{no=4, name='林冲', nickname='豹子头', next=null}}}
HeroNode{no=3, name='吴用', nickname='智多星', next=HeroNode{no=4, name='林冲', nickname='豹子头', next=null}}

发现是我们的next={}出了问题,因为把所有后面的next一起打印出来了 。因为Next也可以说就是指后面的

所以改为如下

@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
再运行即可

 

 上面的是顺序插入,那么我们不顺插而是在中间插入

 中间插入就是

 

 中间插入的好处就是他的顺序即no永远都是从小到大的

代码如下

 //中间插
    public  void addByOrder(HeroNode heroNode)
    {
        //因为节点不能动,任然设置辅助temp
        //我们现在要找的就不是最后节点,而是要插入的前一个节点
        HeroNode temp = head;
        //标识添加的英雄是否已经存在
        boolean flag = false;
        while (true) {
            //说明temp到了链表最后
            if (temp.next == null) {
                break;
            }
            if(temp.next.no>heroNode.no)
            { //位置找到了就在temp的后面
                break;
            }
            else if(temp.next.no==heroNode.no)
            {
                //说明编号存在
                flag=true;
                break;
            }
            temp = temp.next;//后移,遍历链表
    }
        if(flag=true)
        {
            //说明编号存在
            System.out.printf("英雄的编号%d已经存在",heroNode.no);
        }
        else {
            //没有存在,插入进入
            heroNode.next=temp.next;
            temp.next=heroNode;
        }

知道这些以后那么修改也变得简单

   //修改,no不变
    public void update(HeroNode heroNode) {
        //判断是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.next;
        boolean flag = false; //标识是否找到该节点
        while (true) {
            //最后了必须退出,即遍历完也没找到
            if (temp.next == null) {
                break;
            }
            if (temp.no == heroNode.no) {
                //找到了
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag=true)
        {
            temp.name=heroNode.name;
            temp.nickname=heroNode.nickname;
        }
        else {
            System.out.printf("没有找到编号%d的节点",heroNode.no);
        }
    }

那么接下来我们再看一看其他的方法等

 

 有了上面这个getLength后再看看一个结合的案例

 

 

 

 那么我们现在再看一个案例              

 

 

 

 

 

大概思路就是

1重新再来一个链表

2然后遍历原来的链表并取出中间插入新链表的前面位置及head->next,这样就实现了反序

3原来链表的头部信息复制到新链表即可

 

posted @ 2020-05-25 19:00  To_Yang  阅读(232)  评论(0编辑  收藏  举报