数据结构-单链表的操作(增、删、改、翻转、不破坏结构逆序输出、两个单链表合并之后有序输出)

本实例主要是对java中的单链表进行操作

package com.bigdata.datastructure.linkedlist;

import java.util.Stack;

/**
 * @ description:
 * @ author: spencer
 * @ date: 2020/10/27 10:16
 */
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, "赵信", "菊花");

        HeroNode hero5 = new HeroNode(5, "盖伦", "德玛");
        HeroNode hero6 = new HeroNode(6, "吴用", "智多星");
        HeroNode hero7 = new HeroNode(7, "吕布", "皇子");
        HeroNode hero8 = new HeroNode(8, "盲僧", "瞎子");

        // 创建链表
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        SingleLinkedList singleLinkedList2 = new SingleLinkedList();

        // 不按照编号添加
//        singleLinkedList.add(hero1);
//        singleLinkedList.add(hero2);
//        singleLinkedList.add(hero6);
//        singleLinkedList.add(hero4);
//
//        singleLinkedList2.add(hero8);
//        singleLinkedList2.add(hero5);
//        singleLinkedList2.add(hero7);
//        singleLinkedList2.add(hero3);

        // 按照编号顺序添加
        singleLinkedList.addByOrder(hero1);
        singleLinkedList.addByOrder(hero2);
        singleLinkedList.addByOrder(hero6);
        singleLinkedList.addByOrder(hero3);

        singleLinkedList2.addByOrder(hero5);
        singleLinkedList2.addByOrder(hero4);
        singleLinkedList2.addByOrder(hero7);
        singleLinkedList2.addByOrder(hero8);


        System.out.println("修改之前的链表singleLinkedList:");
        singleLinkedList.list();
        System.out.println("修改之前的链表singleLinkedList2:");
        singleLinkedList2.list();

//        HeroNode newHero = new HeroNode(2, "小卢2", "玉麒麟嘻嘻");
//        singleLinkedList.update(newHero);
//        System.out.println("修改后的链表:");
//        singleLinkedList.list();

//        singleLinkedList.delete(4);
//        System.out.println("根据编号删除后的链表:");
//        singleLinkedList.list();
//
//        // 测试单链表中有效节点的个数
//        HeroNode head = singleLinkedList.getHead();
//        System.out.println("有效的节点个数 = " + getLength(head));
//
//        // 测试单链表中倒数第k个节点
//        System.out.println("result = " + findLastIndexNode(head, 4));

        // 测试单链表的翻转
//        System.out.println("单链表的翻转:");
//        reverseList(singleLinkedList.getHead());
//        singleLinkedList.list();

        // 测试单链表的逆序输出
//        System.out.println("测试单链表的逆序输出,没有改变链表的结构");
//        reversePrintList(singleLinkedList.getHead());

        // 测试两个无序单链表的合并,合并之后的链表有序
        System.out.println("测试两个无序单链表的合并,合并之后有序");
        HeroNode mergeHead = merge(singleLinkedList.getHead(), singleLinkedList2.getHead());
        while (mergeHead.next != null){
            System.out.println(mergeHead.next);
            mergeHead = mergeHead.next;
        }

    }
 /**
     * 实现两个单链表的合并,每个单链表无序,合并后的单链表输出有序。
     * 思路:
     * 1.先将两个单链表转换成有序的单链表addByOrder
     * 1.创建一个新的链表,用于存放从单链表中取出的数据
     * 2.遍历两个单链表,将编号较小的数据存入新链表中
     * @param head1 第一个无序单列表
     * @param head2 第二个无序单列表
     * @return 返回新的有序列表的头节点
     */
    public static HeroNode merge(HeroNode head1, HeroNode head2){
        // 创建一个新链表,用于存放两个单链表中取出的数据
        SingleLinkedList singleLinkedList = new SingleLinkedList();

        if (head1.next == null && head2.next == null){
            return null;
        }

        if (head1.next == null){
            return head2;
        }

        if (head2.next == null){
            return head1;
        }

        HeroNode temp1 = head1.next;
        HeroNode temp2 = head2.next;

        // 遍历两个单链表
        while (temp1 != null && temp2 != null) {
            if (temp1.no <= temp2.no) {
                singleLinkedList.add(new HeroNode(temp1.no, temp1.name, temp1.nickName));
                temp1 = temp1.next;

            } else {
                singleLinkedList.add(new HeroNode(temp2.no, temp2.name, temp2.nickName));
                temp2 = temp2.next;

            }
        }
        // 如果temp1为空,则直接插入temp2
        if (temp1 == null){
            while (temp2 != null){
                singleLinkedList.add(new HeroNode(temp2.no, temp2.name, temp2.nickName));
                temp2 = temp2.next;
            }
        }
        // 如果temp2为空,则直接插入temp1
        if (temp2 == null){
            while (temp1 != null){
                singleLinkedList.add(new HeroNode(temp1.no, temp1.name, temp1.nickName));
                temp1 = temp1.next;
            }
        }

        return singleLinkedList.getHead();

    }

    /**
     * 单链表的逆序打印【百度面试题】
     * 思路:
     * 1.先将链表翻转,然后遍历输出,但是会破坏原来链表的结构,不建议
     * 2.使用栈,遍历原来的链表,将遍历出来的节点压入栈中,利用栈的先进后出特点进行打印
     */
    public static void reversePrintList(HeroNode head){
        if (head.next == null || head.next.next == null){
            return;
        }
        HeroNode temp = head;
        Stack<HeroNode> stack = new Stack<HeroNode>();
        // 将链表的所有节点压入栈
        while (temp.next != null){
            stack.push(temp.next);
            temp = temp.next;
        }

        // 弹出栈中的元素
        while (stack.size() > 0){
            System.out.println(stack.pop());
        }

    }

    /**
     * 单向链表翻转【腾讯面试】
     * 思路:
     *  遍历原来的链表,每遍历一个元素,将其取出,添加到新链表reverseHead 的最前端
     * @param head
     * @return
     */
    public static void reverseList(HeroNode head){
        // 如果链表为空或者只有一个元素,则直接返回
        if(head.next == null || head.next.next ==null){
            return ;
        }

        // 定义一个辅助的变量,用来遍历原来的链表
        HeroNode cur = head.next;
        HeroNode next = null; // 指向当前节点【cur】的下一个节点
        HeroNode reverseHead = new HeroNode(0, "", "");

        // 遍历原来的链表,每遍历一个元素,将其取出,添加到新链表reverseHead 的最前端
        while (cur != null){
            next = cur.next; // 先保存当前节点的下一个节点(因为取出当前节点之后,下一次取出的节点需要记录)
            cur.next = reverseHead.next; // 将cur的下一个节点指向新链表的最前端
            reverseHead.next = cur; // 将cur连接到新的链表上
            cur = next; // 移动cur,方便下次继续取出
        }
        // 最后将head.next = reverseHead.next,实现翻转
        head.next = reverseHead.next;
    }

    /**
     * 查找单链表中倒数第k个节点【新浪面试题】
     * 思路
     * 1.编写一个方法,接收heda节点,同时接收一个index
     * 2.index表示倒数第index节点
     * 3.先把链表从头到尾遍历,得到链表的总长度getLength
     * 3.得到size后,我们从链表的第一个开始遍历到第(size-index)个,就可以满足需求
     *
     * @param head
     * @param index
     * @return
     */
    public static HeroNode findLastIndexNode(HeroNode head, int index){
        if (head.next == null){
            return null;
        }
        int size = getLength(head);
        // 对index进行校验
        if (index < 0 || index > size){
            return null;
        }
        HeroNode cur = head.next;
        for (int i = 0; i < size - index; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /**
     * 获取单链表节点的个数(如果有头结点,不统计,因为头节点不存储数据)
     * @param head 链表的头结点
     * @return 返回的是链表中有效节点的个数
     */
    public static int getLength(HeroNode head){
        if (head.next == null){
            return 0;
        }
        HeroNode temp = head;
        int length = 0;
        while (temp.next != null){
            length++;
            temp = temp.next;
        }

        return length;
    }
}

// 创建单链表管理HeroNode对象(不考虑编号顺序,每次在链表最后添加节点)
class SingleLinkedList{
    // 先初始化一个头节点,不存放具体的数据
    private HeroNode head = new HeroNode(0, "", "");

    // 返回头结点
    public HeroNode getHead() {
        return head;
    }

    // 添加节点到链表
    // 思路:找到链表的最后一个节点,将最后一个节点的next指向该节点
    public void add(HeroNode heroNode){

        // 因为head节点不能动,所有需要一个辅助变量来替代head节点
        HeroNode temp = head;
        while (true){
            if (temp.next == null){
                break;
            }
            // 如果没有找到,将temp后移
            temp = temp.next;
        }
        // 当退出while循环时,temp就指向了链表的最后
        temp.next = heroNode;

    }

    // 第二种添加方式,通过排名添加节点到链表指定位置
    // 如果有这个排名,则添加失败,并给出提示信息
    public void addByOrder(HeroNode heroNode){
        // 头节点不能移动,需要使用辅助变量
        // 因为需要找到的temp是添加位置的前一个节点,否则添加节点失败
        HeroNode temp = head;

        boolean flag = false; // 表示添加的编号是否存在,默认为false
        while (true){
            if (temp.next == null){ // 遍历到链表的最后
                break;
            }else if (temp.next.no > heroNode.no){ // 找到添加的位置
                break;
            }else if (temp.next.no == heroNode.no){ // 编号已经存在
                flag = true;
                break;
            }
            // 将辅助变量后移遍历
            temp = temp.next;
        }

        // 判断flag的值
        if (flag){ // 说明编号已经存在
            System.out.printf("需要添加的英雄编号 %d 已经存在,不能继续添加", heroNode.no);
            return;
        } else {
            // 可以插入到链表中
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    // 根据编号修改heroNode对象
    public void update(HeroNode heroNode){

        if (head.next == null){
            return;
        }
        // 找到需要修改的节点(不需要找到修改节点的前一个节点)
        HeroNode temp = head;
        boolean flag = false; // 表示是否找到该节点
        while (true){
            if (temp == null){ // 已经遍历完链表
                break;
            }
            if (temp.no == heroNode.no){
                // 找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        // 根据flag判断是否找到要修改的节点
        if(flag){
            temp.name = heroNode.name;
            temp.nickName = heroNode.nickName;
        } else {
            // 没有找到
            System.out.printf("没有找到编号为 %d 的节点,不能修改\n", heroNode.no);
        }
    }

    // 删除节点
    public void delete(int no){
        if (head.next == null){
            System.out.println("链表为空,不能删除数据");
            return;
        }

        HeroNode temp = head;
        boolean flag = false;
        while (true){
            if (temp.next == null){ //遍历完链表都没找到
                break;
            }
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        // 根据编号找到要删除的节点
        if (flag){
            temp.next = temp.next.next;
        } else {
            System.out.println("要删除的节点编号不存在,无法删除");
        }
    }



    // 显示链表(遍历)
    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.next;
        }
    }

}

// 创建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;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值