编程语言
首页 > 编程语言> > 单链表相关操作【java】

单链表相关操作【java】

作者:互联网

单链表相关操作

  1. 单链表的创建、添加、修改、删除
  2. 常见面试题:
    1)求单链表中有效结点个数
    2)查找单链表中的倒数第k个结点
    3)单链表的反转
    4)从尾到头打印单链表
    5)合并两个有序链表
package com.atguigu.demomptest.linkedlist;

import java.util.Date;
import java.util.Stack;

import static com.atguigu.demomptest.linkedlist.SingleLinkedList.*;

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

        HeroNode hero5 = new HeroNode(5, "a", "aa");
        HeroNode hero6 = new HeroNode(6, "b", "bb");
        HeroNode hero7 = new HeroNode(7, "c", "cc");
        HeroNode hero8 = new HeroNode(8, "d", "dd");

/*        //创建链表,加入英雄
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(hero1);
        singleLinkedList.add(hero4);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);

//        singleLinkedList.addByOrder(hero1);
//        singleLinkedList.addByOrder(hero4);
//        singleLinkedList.addByOrder(hero2);
//        singleLinkedList.addByOrder(hero3);
//        singleLinkedList.addByOrder(hero3);

*//*        // 修改前
        singleLinkedList.list();

        // 测试修改
        HeroNode heroNode = new HeroNode(2, "小卢", "玉麒麟~~");
        singleLinkedList.update(heroNode);

        System.out.println("修改后的链表情况~");
        //显示
        singleLinkedList.list();*//*

        //测试删除
//        System.out.println("删除前的链表~~~");
//        singleLinkedList.list();
//        singleLinkedList.delete(1);
//        singleLinkedList.delete(4);
//        System.out.println("删除后的链表~~~");
//        singleLinkedList.list();

        // 单链表的有效结点个数
        System.out.println("有效结点个数=" + getLength(singleLinkedList.getHead()));

        //测试是否得到了倒数第k个结点
        HeroNode res = findLastIndexNode(singleLinkedList.getHead(), 1);
        System.out.println("res=" + res);

        //测试单链表反转
        System.out.println("反转前的链表==============");
        singleLinkedList.list();
        reverseList(singleLinkedList.getHead());
        System.out.println("反转后链表===================");
        singleLinkedList.list();

        //测试逆序打印单链表
        System.out.println("==========逆序打印单链表,链表结构不变==============");
        reversePrint(singleLinkedList.getHead());*/

        //测试有序链表合并
        SingleLinkedList s1 = new SingleLinkedList();
        s1.addByOrder(hero1);
        s1.addByOrder(hero3);
        s1.addByOrder(hero5);
        s1.addByOrder(hero8);
        SingleLinkedList s2 = new SingleLinkedList();
        System.out.println("链表的头是否相等"+(s1.getHead()==s2.getHead()));
        s2.addByOrder(hero2);
        s2.addByOrder(new HeroNode(5,"aaa","aaaaa"));
        s2.addByOrder(hero7);
    //    s2.add(hero8);
        System.out.println("h1:");
        s1.list();
        System.out.println("h2:");
        s2.list();

        //方法一:
//        mergeList(s1.getHead(), s2.getHead());
        //方法二:递归
        mergeList1(s1.getHead(),s2.getHead());
        System.out.println("合并后的链表:::");
        s1.list();


    }
}


//定义单链表管理英雄结点
class SingleLinkedList {
    //创建头结点
    private HeroNode head = new HeroNode(0, "", "");

    public HeroNode getHead() {
        return head;
    }

    //向链表中插入数据思路:
    //1、找到链表的末尾,
    //2、将最后结点的next 指向新的结点
    public void add(HeroNode heroNode) {
        //因为head头结点不能东,因此需要定义一个辅助变量
        HeroNode temp = head;
        while (true) {
            if (temp.next == null) {//说明找到链表的最后
                break;
            }
            //还没找到链表最后,temp继续往后找
            temp = temp.next;
        }
        //跳出循环,说明找到链表最后,将数据插入
        temp.next = heroNode;
    }

    //第二方式添加英雄,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
    //1、首先找到新添加的结点的位置,是通过辅助变量(指针)
    //2、新的结点.next=temp.next
    //3、temp.next=新的结点
    public void addByOrder(HeroNode heroNode) {
        //因为head头结点不能动,因此需要定义一个辅助变量来帮助找到添加的位置
        //因为单链表,因此我们找的temp是位于 添加位置的前一个结点
        HeroNode temp = head;

        //标志:添加的编号是否存在,默认为false
        boolean flag = false;
        while (true) {
            if (temp.next == null) {//说明找到链表的最后
                break;
            }
            if (temp.next.no > heroNode.no) {//因为是从头结点开始比较,找到第一个比heroNode.no大的no,然后把heroNode插入前面。
                // 不能和temp.no比较,因为temp没有前置指针,没办法将heroNode插入temp的前面
                //说明找到了
                break;
            } else if (temp.next.no == heroNode.no) {// 说明希望添加的heroNode的编号已存在
                flag = true; //说明编号已存在
                break;
            }
            //还没找到插入的位置,temp继续往后找
            temp = temp.next;
        }
        //跳出循环,判断flag的值
        if (flag) {
            System.out.printf("准备插入的英雄的编号 %d 已经存在,不能加入\n", heroNode.no);
        } else {
            //插入到链表中,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
        temp.next = heroNode;
    }

    //修改链表,no不能修改
    public void update(HeroNode newHeroNode) {
        // 判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        //定义辅助变量
        HeroNode temp = head;
        boolean flag = false;//是否找到要修改的英雄
        while (true) {
            if (temp.next == null) {
                System.out.println("遍历完链表还没找到");
                break;
            }
            if (temp.next.no == newHeroNode.no) {//找到了要修改的英雄
                flag = true;
                break;
            }
            // temp后移,遍历链表
            temp = temp.next;
        }
        if (flag) {//找到了
            temp.next.name = newHeroNode.name;
            temp.next.nickname = newHeroNode.nickname;
        } else {
            System.out.printf("不存在编号为 %d 的英雄,不能修改", newHeroNode.no);
        }

    }

    //删除,因为是单向链表,因此要找到要删除结点的前一个结点,temp.next=temp.next.next
    public void delete(int no) {
        HeroNode temp = head;
        boolean flag = false;//是否找到要删除的英雄

        //找到要删除结点的前一个结点
        while (true) {
            if (temp.next == null) {
                System.out.println("链表遍历结束");
                break;
            }
            if (temp.next.no == no) { //找到了
                flag = true;
                break;
            }
            //还没找到,temp后移
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
        } else {
            System.out.printf("没有找到要编号为 %d 的英雄,删除失败", no);
        }

    }

    //求单链表中有效结点的个数
    /**
     * @param head 单链表的头结点
     * @return 有效结点个数
     */
    public static int getLength(HeroNode head) {
        //定义辅助变量
        HeroNode curr = head.next;
        int length = 0;
        while (curr != null) {
            length++;
            curr = curr.next; //遍历
        }
        return length;
    }

    //查找单链表中的倒数第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 temp = head.next;
        for (int i = 0; i < size - index; i++) { //目的是让指针后移
            temp = temp.next;
        }
        return temp;
    }

    //单链表反转
    //思路:头插法
    //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 reverseHead = new HeroNode(0, "", "");
        //遍历原来的链表
        //定义辅助变量cur
        HeroNode cur = head.next; // 从第一个有效结点开始
        HeroNode next = null; //指向当前结点的下一个结点
        while (cur != null) {
            next = cur.next; //每次都要先保存当前结点的下一个结点,否则原来的链表会断链,无法继续遍历下去
            // 将当前结点取出,并放在新链表的最前面
            cur.next = reverseHead.next;
            reverseHead.next = cur;
            cur = next;// 指针后移
        }
        //将head.next 指向reverseHead.next,实现链表反转
        head.next = reverseHead.next;
    }

    //从尾到头打印单链表
    //思路
    //方式一:先将单链表反转,然后再遍历。会破坏原来的单链表结构,不建议
    //方式二:可以利用栈。将各个结点压入栈中,然后利用栈后进先出的特点,实现逆序打印的效果

    public static void reversePrint(HeroNode head) {
        if (head.next == null) {
            return;
        }
        //创建一个栈,将各个结点压入栈
        Stack<HeroNode> stack = new Stack<>();
        //定义辅助变量
        HeroNode cur = head.next;
        while (cur != null) {
            stack.push(cur); //入栈
            cur = cur.next;//指针后移,压入下一个结点
        }
        // 将栈中的结点pop
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
    }

    //合并两个有序链表:思路和单链表反转类似
    public static void mergeList(HeroNode h1,HeroNode h2){
        System.out.println("进入了merge方法。h1.next="+h1.next);

        HeroNode cur1 = h1.next;
        HeroNode cur2 = h2.next;
        HeroNode next1=null;
        HeroNode next2=null;

        HeroNode mergeHead = new HeroNode(0,"","");
        HeroNode temp = mergeHead;

        System.out.println("cur1==null:::"+cur1==null);
        System.out.println("cur2==null:::"+cur2==null);
        while(true){
            if (cur1==null){
                temp.next=cur2;
                System.out.println("第一个单链表遍历完了,cur2:"+cur2+",cur2.next:"+cur2.next);
                break;
            }
            if (cur2==null){
                temp.next=cur1;
                System.out.println("第二个单链表遍历完了");
                break;
            }
            if (cur1.no<cur2.no){
                next1 = cur1.next;
                cur1.next=null;
                temp.next=cur1;
                temp = temp.next;
                cur1=next1;
                System.out.println("h1::"+mergeHead.next);
            }else{
                next2 = cur2.next;
                cur2.next=null;
                temp.next=cur2;
                temp = temp.next;
                cur2=next2;
                System.out.println("h2:::"+mergeHead.next);
            }
        }
        h1.next=mergeHead.next;
    }

    //合并两个有序链表:递归
    public static HeroNode mergeList1(HeroNode h1,HeroNode h2){
        if (h1==null){
            return h2;
        }else if (h2==null){
            return h1;
        }else if (h1.no<h2.no){
            h1.next=mergeList1(h1.next,h2);
            return h1;
        }else {
            h2.next = mergeList1(h1,h2.next);
            return h2;
        }
    }


    //显示链表【遍历】
    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;
        }

    }

}

//定义英雄节点对象
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 + '\'' +
                '}';
    }
}

标签:结点,单链,java,temp,HeroNode,next,链表,操作,null
来源: https://blog.csdn.net/weixin_41974036/article/details/121040010