单链表(single linked list)

链表介绍(linked list)

链表是有序的列表,但它在内存中是如下存储的:

1.png

小结:

  1. 链表是以节点的方式来存储,是链式存储
  2. 每个节点包含data域,next域:指向下一个节点.
  3. 如图:发现链表的各个节点不一定是连续存储.
  4. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

单链表(带头节点)逻辑结构如下:

2.png

单链表应用实例

使用带head头的单向链表实现-水浒英雄排行榜管理,完成对英雄人物的增删改查操作.

  1. 第一种方法在添加英雄时,直接添加到链表的尾部
3.png
  1. 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
4.png
  1. 修改节点功能

    修改节点信息,根据no来修改,即no不能改 temp是要修改的节点

  2. 删除节点功能

     删除节点,根据no来删除 temp是要删除节点的前一个节点
    
5.png
  1. 代码实现:
package com.atguigu.linkedlist;

import java.util.Stack;

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, "李逵", "黑旋风");

        // 创建一个单链表
//      SingleLinkedList singleLinkedList = new SingleLinkedList();
//      singleLinkedList.add(hero1);
//      singleLinkedList.add(hero2);
//      singleLinkedList.add(hero3);
//      singleLinkedList.add(hero4);

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

        // 修改节点信息
//      singleLinkedList.update(2, "小卢", "小麒麟");

        // 删除节点
//      singleLinkedList.delete(1);
//      singleLinkedList.delete(5);

//      singleLinkedList.show();

        // 单链表的大小
//      System.out.println(singleLinkedList.size());

        // 查找单链表中倒数第k个节点
//      System.out.println(singleLinkedList.findNode(1));
//      System.out.println(singleLinkedList.findNode(3));
//      System.out.println(singleLinkedList.findNode(5));

        // 反向遍历
//      singleLinkedList.lastToFirst();

        // 单链表的反转
//      singleLinkedList.reverse();

        // 合并两个有序链表
        SingleLinkedList singleLinkedList1 = new SingleLinkedList();
        singleLinkedList1.addByOrder(hero1);
        singleLinkedList1.addByOrder(hero3);
        singleLinkedList1.addByOrder(hero5);
        SingleLinkedList singleLinkedList2 = new SingleLinkedList();
        singleLinkedList2.addByOrder(hero2);
        singleLinkedList2.addByOrder(hero4);
        merge(singleLinkedList1, singleLinkedList2).show();
    }

    /**
     * 单链表的反转,原理是头插法
     * 
     * @param singleLinkedList
     */
    public static void reverse(SingleLinkedList s) {
        // 如果当前链表为空,或者只有一个节点,无需反转,直接返回
        if (s.head.next == null || s.head.next.next == null) {
            return;
        }
        HeroNode cur = s.head.next;
        HeroNode next = null;// 指向当前节点[cur]的下一个节点
        HeroNode newHead = new HeroNode();
        // 遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表newHead的最前端
        while (cur != null) {
            next = cur.next;// 先暂时保存当前节点的下一个节点,因为后面需要使用
            cur.next = newHead.next;// 将cur放在新链表的最前端
            newHead.next = cur;
            cur = next;// cur后移
        }
        // 将head.next指向newHead.next,实现单链表的反转
        s.head.next = newHead.next;
    }

    /**
     * 从尾到头打印单链表,利用栈
     * 
     * @param s
     */
    public static void lastToFirst(SingleLinkedList s) {
        if (s.head.next == null) {// 空链表
            return;
        }
        // 创建一个栈,将各个节点压入栈
        Stack<HeroNode> stack = new Stack<HeroNode>();
        HeroNode cur = s.head.next;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        // 出栈
        while (!stack.isEmpty()) {
            System.out.println(stack.pop());
        }
    }

    /**
     * 合并两个有序的单链表,合并之后的链表依然有序,原理是尾插法
     * 
     * @param s1 有序链表1
     * @param s2 有序链表2
     */
    public static SingleLinkedList merge(SingleLinkedList s1, SingleLinkedList s2) {
        SingleLinkedList newSingleLinkedList = new SingleLinkedList();
        HeroNode tail = newSingleLinkedList.head;// 链表尾
        HeroNode cur1 = s1.head.next, cur2 = s2.head.next;
        // 当cur1与cur2同时都有值时才继续移动,一旦有一段为空了,说明另一段剩下的都比它大,直接接到新list上就可以了,减少循环浪费
        while (cur1 != null && cur2 != null) {
            if (cur1.no < cur2.no) { // 将cur1节点插入到newHead链表的尾部
                tail.next = cur1;
                cur1 = cur1.next;
            } else { // 将cur2节点插入到newHead链表的尾部
                tail.next = cur2;
                cur2 = cur2.next;
            }
            tail = tail.next;// 移动tail,指向最后一个节点
        }
        // 上述循环结束后,一定有cur1或者cur2是空的,那么判断并将另一段直接接入到tail后边
        if (cur1 == null) {
            tail.next = cur2;
        }
        if (cur2 == null) {
            tail.next = cur1;
        }
        return newSingleLinkedList;
    }
}

//定义SingleLinkedList,管理链表
class SingleLinkedList {
    // 先初始化一个头节点,头节点不要动,不存放具体的数据
    public HeroNode head = new HeroNode();

    /**
     * 添加节点,当不考虑编号顺序时:
     * <p>
     * 1.找到当前链表的最后节点 2.将最后节点的next指向新节点
     * 
     * @param heroNode 新节点
     */
    public void add(HeroNode heroNode) {
        // 1.找到当前链表的最后节点
        HeroNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        // 此时temp是链表最后一个节点,将其next指向新节点
        cur.next = heroNode;
    }

    /**
     * 按照编号顺序添加,如果编号已存在,则添加失败,并给出提示
     * 
     * @param heroNode 新节点
     */
    public void addByOrder(HeroNode heroNode) {
        HeroNode cur = head;
        while (cur.next != null) {
            if (cur.next.no > heroNode.no) {
                // 此时新节点的位置就是temp的后面
                heroNode.next = cur.next;
                cur.next = heroNode;
                return;
            }
            if (cur.next.no == heroNode.no) {
                System.out.println("编号" + heroNode.no + "已存在");
                return;
            }
            // 一定要记住temp后移
            cur = cur.next;
        }
        // 如果temp是最后节点,还没有return,则新节点插入到最后
        cur.next = heroNode;
    }

    /**
     * 修改节点信息,根据no来修改,即no不能改 cur是要修改的节点
     * 
     * @param no       编号
     * @param name     新name
     * @param nickname 新nickname
     */
    public void update(int no, String name, String nickname) {
        HeroNode cur = head.next;
        while (cur != null) {
            if (cur.no == no) {
                cur.name = name;
                cur.nickname = nickname;
                return;
            }
            cur = cur.next;
        }
        System.out.println("没有找到编号为" + no + "的节点");
    }

    /**
     * 删除节点,根据no来删除 cur是要删除节点的前一个节点
     * 
     * @param no 编号
     */
    public void delete(int no) {
        HeroNode cur = head;
        while (cur.next != null) {
            if (cur.next.no == no) {
                // 将要删除的节点的前一个节点指向要删除节点的后一个节点
                cur.next = cur.next.next;
                return;
            }
            cur = cur.next;
        }
        System.out.println("没有找到编号为" + no + "的节点");
    }

    // 显示链表,遍历
    public void show() {
        if (head.next == null) {
            System.out.println("链表为空");
        } else {
            HeroNode cur = head.next;
            while (cur != null) {
                System.out.println(cur);
                cur = cur.next;
            }
        }
    }

    /**
     * 求单链表中有效节点的个数,不统计头节点
     * 
     * @return
     */
    public int size() {
        HeroNode cur = head.next;
        int sum = 0;
        while (cur != null) {
            sum++;
            cur = cur.next;
        }
        return sum;
    }

    /**
     * 查找单链表中倒数第k个节点,并返回
     * 
     * @param k 倒数
     * @return
     */
    public HeroNode findLastKNode(int k) {
        int count = size() + 1;
        HeroNode cur = head.next;
        while (cur != null) {
            // 每找到一个节点,count--,如果count=k,则即为倒数第k个节点
            if (--count == k) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

}

//定义HeroNode,每个HeroNode对象就是一个节点
class HeroNode {
    int no;
    String name;
    String nickname;
    HeroNode next;// 指向下一个节点

    public HeroNode() {
    }

    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 + "]";
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容