链表-单向链表

链表 (Linked List)

1.概述:

1、 链表是以结点的方式来存储,是链式存储
2、 每个结点包含data域,next域;指向下一个结点
3、 链表的各个结点不一定是连续地址存放
4、链表分带头结点的链表和没有头结点的链表,根据实际的需求来确定
在这里插入图片描述

2.单向链表的创建、把结点添加到链表尾部(尾插法)

缺点:只能根据添加的前后顺序来输出显示

2.1 定义一个结点类

代码如下:

// 定义HeroNode,每个HeroNode的实例对象就是一个结点
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;
    }

    // 为了显示方便,重写toString方法
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\''  +
                '}';
    }
}

2.2 定义链表类,以及添加和显示的功能

添加(创建):
1、先创建一个head头结点,作用是表示单链表的头
2、后面我们每添加一个结点,就直接加入到链表的最后

遍历:
通过一个辅助变量遍历,帮助遍历整个链表

代码如下:

// 定义SingleLinkedList管理我们的英雄
class SingleLinkedList{
    // 先初始化一个头结点,头结点不要动,不存放具体的数据
    private HeroNode head= new HeroNode(0,"","");

    // 添加节点到单向链表
    // 思路,当不考虑编号顺序时
    // 1、找到当前链表的最后节点  2、将最后这个结点的next指向新的结点
    public void add1(HeroNode heroNode){
        // 因为head结点不能动,因此需要一个辅助遍历temp
        HeroNode temp =head;
        // 遍历链表,找到最后结点
        while (true){
            // 找到链表的最后
            if (temp.next==null){
                break;
            }
            // 如果没有找到最后,将temp后移
            temp=temp.next;
        }
        // 当推出while循环时,temp就只想了链表的最后
        temp.next=heroNode;
    }

    // 显示链表【遍历】
    public void showlist(){
        // 判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        // 因为头结点,不能动,因此要一个辅助遍历temp来遍历
        HeroNode temp=head.next;
        while (true){
            // 判断是否到链表最后
            if (temp==null){
                break;
            }
            //输出结点信息
            System.out.println(temp);
            // 将temp后移,一定要小心
            temp=temp.next;
        }
    }
}

2.3 测试自定义链表,以及添加和显示的功能

代码如下:

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        // 进行测试
        // 先创建结点
        HeroNode sj = new HeroNode(1, "宋江", "及时雨");
        HeroNode ljy = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode wy = new HeroNode(3, "吴用", "智多星");
        HeroNode lc = new HeroNode(4, "林冲", "豹子头");

        // 创建单向链表
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        // 加入
        singleLinkedList.add1(sj);
        singleLinkedList.add1(ljy);
        singleLinkedList.add1(wy);
        singleLinkedList.add1(lc);

        // 显示
        singleLinkedList.showlist();
    }
}

3.单向链表的创建、把结点添加到链表中间(指定结点后插法)

优势:可以无视添加的先后顺序,根据自己定义的节点编号(no)大小顺序来显示

说明:

需要按照编号的顺序添加
1、首先找到新添加的结点的位置,是通过辅助变量temp(指针),遍历搞定。
2、(新的结点.next)=temp.next
3、将temp.next=新结点

代码改动如下:

// 第二种方式在添加结点时,根据顺序排名将结点插入到指定位置
    // 如果有这个排名,则添加失败,并给提示
    public void add2(HeroNode heroNode){
        // 因为head结点不能动,因此需要一个辅助temp 遍历到要添加的位置
        // 因为单链表,找的temp位于添加位置的前一个结点,否则插入不了(指定位置后插)
        HeroNode temp =head;
        boolean flag=false;// 表示添加的结点的编号是否存在,默认false
        // 遍历到指定位置
        while (true){
            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){    // flag为true时不可以添加
            System.out.printf("结点的编号%d已存在不可以再添加 ",heroNode.no);
        }else{
        // 插入到链表中 temp后(指定位置后面)
         heroNode.next=temp.next;
         temp.next=heroNode;
        }
    } 

测试代码改动如下:

// 第二种方式添加  (根据no来排列)
        singleLinkedList.add2(sj);
        singleLinkedList.add2(lc);
        singleLinkedList.add2(wy);
        singleLinkedList.add2(ljy);

4.—添加修改指定编号结点信息的方法

修改节点的信息,根据no编号来修改,即no编号不能改 说明:根据newHeroNode 的no来修改

添加代码如下:

// 修改节点的信息,根据no编号来修改,即no编号不能改
    // 说明:根据newHeroNode 的no来修改
    public void updata(HeroNode newHeroNode){
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        HeroNode 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= newHeroNode.name;
            temp.nickname=newHeroNode.nickname;
        }else {
            System.out.printf("没有找到编号%d的结点 ",newHeroNode.no);
        }

   }

测试代码如下:

// 测试修改节点的代码
        HeroNode newheroNode = new HeroNode(2, "小卢", "玉麒麟~~");
        singleLinkedList.updata(newheroNode);

        // 显示
        System.out.println("修改后的情况...");
        singleLinkedList.showlist();

5.添加删除结点的方法

从单链表中删除一个结点的思路==
1、先找到需要删除的这个节点(根据no判断)的前一个结点temp
2、temp.next=temp.next,next
3、被删除的节点,将不会有其他引用指向,将会被垃圾回收机制回收。

添加代码如下:

//根据no值来判断删除
   public void del(int no){
       HeroNode temp=head;
       boolean flag=false;  //  标识是否找到
       while (true){
           if (temp.next==null){ //到最后
               break;
           }
           if (temp.next.no==no) {
               // 找到待删除结点的前一个结点temp
               flag = true;
               break;

           }
           temp=temp.next; // temp后移,继续遍历
       }
       if (flag){
           temp.next=temp.next.next;
       }else {
           System.out.printf("要删除的编号%d的结点不存在\n",no);
       }
   }

测试代码如下:

        // 删除一个结点
        singleLinkedList.del(1);
        System.out.println("删除后的情况...");
        singleLinkedList.showlist();

6.单向链表面试题

6.1求单链表中有效结点的个数

概述:

方法:获取到单链表有效结点的个数(有头结点的要去掉
head 链表的头结点 return是返回的有效结点的个数

代码如下:

public static int getLength(HeroNode head){
        if(head.next==null){  // 如果头结点下一个为null返回0
            return 0;
        }
        int length=0;
        HeroNode temp=head;
        while (true){
            if (temp.next==null){
                break;
            }
            temp=temp.next;
            length++;
        }
        return length;
    }

// 测试一下统计结点个数

 System.out.println("有效的结点个数为:" + SingleLinkedList.getLength(singleLinkedList.getHead()));

6.2查找单链表中倒数第k个结点

思路说明:

1、编写一个方法,时接受一个index
2、index表示是倒数第index个结点
3、先把链表遍历一遍,得到链表长度m,然后再遍历(m-index) 使要查找的结点对应的next(下一个)就是要查找的倒数k个结点

实现代码如下:

public  HeroNode  findneedIndex(int index){
        if(head.next==null){
            return null;  // 没有找到
        }
        int m=getLength();   // 获取链表长度
        HeroNode temp=head;
        if(index<=0 || index>m ){
			return null;
		}
        for (int i=1;i<=(m-index);i++){
            temp=temp.next;
        }
        return temp.next;
    }

// 测试一下查找倒数第k个结点

System.out.println("输出的倒数第k个结点是:"+ singleLinkedList.findneedIndex(1));      

6.3单链表的反转(头插法)

思路如下:

1、先定义一个头结点reverseHead=new HeroNode(0,“”,“”);—一个只有头结点的新链表
2、从头到尾遍历原来的链表,每遍历一个结点,就将其取出,并放在新的链表reverseHead的最前端。

next = temp.next;// 保存temp后的那个结点,后面移位要使用

temp.next = reversehead.next; // 取下的temp指向新链表头结点指向的结点(null)。

reversehead.next = temp; // 将新链表的头结点指向取下的temp结点

temp = next; // 把temp移到保存再next中它后面的那个结点

实现代码如下:

public void reverseHead(){
        if (head.next==null || head.next.next==null){
            return;
        }
        HeroNode temp=head.next;
        HeroNode next =null; // 指向当前结点【cur】的下一个结点
        HeroNode reversehead= new HeroNode(0,"","");
        // 从头到尾遍历原来的链表,没遍历一个结点,就将其取出,
        // 并放在新的链表reverseHead的最前端。
        while (temp!=null) {
            next = temp.next;// 保存temp后的那个结点,后面移位要使用
            temp.next = reversehead.next; // 取下的temp指向新链表头结点指向的结点(null)。
            reversehead.next = temp; // 将新链表的头结点指向取下的temp结点
            temp = next;  // 把temp移到保存再next中它后面的那个结点
        }
        // 将head.next指向reversehead.next,实现头结点的替换
        head.next=reversehead.next;
    }

测试如下:

// 测试单链表的反转功能
        System.out.println("原来的单链表");
        singleLinkedList.showlist();

        System.out.println("反转后的单链表");
        singleLinkedList.reverseHead();
        singleLinkedList.showlist();

6.4从尾到头打印单链表(反向遍历 / stack栈)

思路分析:

方式一:先将单链表进行翻转操作,然后在遍历即可(问题是会破坏原来单链表的结构,不可取
方式二:可以利用栈这个数据结构,将各个结点压入到中,然后利用栈的先进后出的特点,就实现了逆序打印的效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值