03、数据结构与算法--单向链表

一种比顺序表稍微复杂些的结构...


一、认识链表

1、 基本结构

链表是一个个结点构成的,就像火车

顺序表可以通过get方法(传入下标)来获取表,因为它们的地址是连续的

与顺序表不同的是,链表的物理存储不连续,要获取某个结点的话不得不进行遍历


2、手动实现

注意: 1、先绑定后面结点

2、遍历时注意是cur != null还是cur.next != null

java 复制代码
package structure;

public class MyLinkedList {

    public Node head;

    //结点类
    public class Node {

            public int val;
            public Node next;

            public Node(int val) {
                this.val = val;
            }

        }

//    傻瓜式创建,展示一下链表结构
//    public void createList () {
//
//                Node node1 = new Node(12);
//                Node node2 = new Node(24);
//                Node node3 = new Node(36);
//                Node node4 = new Node(48);
//                Node node5 = new Node(50);
//
//                node1.next = node2;
//                node2.next = node3;
//                node3.next = node4;
//                node4.next = node5;
//
//                this.head = node1;
//
//    }


    //打印链表val值
    public void show () {

        Node cur = head;

        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }

        System.out.println();
    }

    //头插法
    public void addFirst(int data) {

        Node n = new Node(data);
        n.next = head;
        head = n;

    }

    //尾插法
    public void addLast(int data) {

        Node n = new Node(data);

        if (head == null){
            head = n;
            //此处没有return会继续执行下面的内容
            return;
        }

        Node cur = head;
        while (cur.next != null){
            cur = cur.next;
        }
        cur.next = n;
    }


    //将数据插入到下标index位置
    public void addIndex(int index, int data) {
        //这样就不用重复调用方法了
        int len = size();

        if(index < 0 || index > len){
            System.out.println("index位置不合法");
            return;
        }

        //插在最前
        if (index == 0){
            addFirst(data);
            return;
        }

       //插到最后
       if (index == len){
           addLast(data);
           return;
       }

       //代表index位置的前一位
        Node cur = head;

//        此写法可读性较差
//        int count = index;
//        while (count - 1 != 0){
//            cur = cur.next;
//            count--;
//        }

        //找到index的前一位cur
        for (int i = 0;i < index-1;i++){
            cur = cur.next;
        }

        Node n = new Node(data);

        //主要逻辑
        n.next = cur.next;
        cur.next = n;

    }


    //查找key值是否在单链表当中
    public boolean contains(int key) {

        Node cur = head;

        while (cur != null){
            if (cur.val == key){
                return true;
            }
            cur = cur.next;
        }
             return false;

    }


//    //删除遇见的第一个值为key的节点
//    public void remove(int key) {
//
//        Node cur = head;
//        Node del = findDel(key);
//
//        //确保头结点不为空
//        if(head == null){
//            return;
//        }
//        //删除头结点
//        if(head == del){
//            head = head.next;
//            return;
//        }
//
//        //优先选择引用比较,比值比较更安全
//        while (cur != del){
//            cur = cur.next;
//        }
//
//        //主要逻辑
//        cur.next = del.next;
//
//    }
//
//    //找到要删除结点del的地址
//    public Node findDel(int key){
//
//            Node i = head;
//
//            while (i != null){
//                if (i.val == key){
//                    return i;
//                }
//                i = i.next;
//            }
//            return null;
//
//    }

    //删除遇见的第一个值为key的节点
    public void remove(int key) {

        Node cur = head;

        //确保头结点不为空
        if(head == null){
            return;
        }
        //删除头结点
        if(head.val == key){
            head = head.next;
            return;
        }

        //优先选择引用比较,比值比较更安全
        while (cur.next != null){
           if (cur.next.val == key){
               //主要逻辑
               Node del = cur.next;
               cur.next = del.next;
               return;
           }
           cur = cur.next;
        }

    }

    //删除所有值为key的节点
    public void removeAllKey(int key) {

//        //先循环处理头结点(可能连续多个)
//        while (head != null && head.val == key){
//            head = head.next;
//        }
//
        //链表为空,直接返回
        if (head == null){
            return;
        }

        Node prev = head;
        Node del = prev.next;

        //主要逻辑
        while (del != null){
            if(del.val == key){
                del = del.next;
                prev.next = del;
            }else {
                del = del.next;
                prev = prev.next;
            }
        }

        //最后再处理头结点,写法更为简洁
        if (head.val == key){
            head = head.next;
        }

    }

    //得到单链表结点的数量
    public int size() {
            int count = 0;
            Node cur = head;
            while (cur != null){
                count++;
                cur = cur.next;
            }

            return count;
        }

    public void clear() {
        head = null;
    }

3、重要方法的逻辑图😊

①头插法


②尾插法


③指定位置的插入


④删除遇见的第一个值为key的结点

遇见key删除后直接返回,没遇见仅prev继续往下走


⑤删除所有值为key的结点(也是面试题目)

遇见要删除的key值,执行删除逻辑:

没遇见要删除的key值,key跟prev都继续往下走:

del一直走过最后一个结点停止,这里的prev一直充当del的前置结点:

直到中间结点处理完以后再处理头结点


二、链表的面试题🙂

1、反转链表

2、分割链表

Ⅰ代码解析

java 复制代码
   //反转链表
    //从传入的newHead头结点开始反转链表
    public Node reverseList(Node newHead) {

        if(newHead == null){
            return newHead;
        }

        //确定cur位置,将传入的头结点next置空
        Node cur = newHead.next;
        newHead.next = null;

        Node curN;

        while(cur != null){
            //保存cur下一个结点的值,然后再修改指向
            curN = cur.next;
            cur.next = newHead;
            //newHead接着往下走,cur来到curN位置
            newHead = cur;
            cur = curN;
        }
        return newHead;
        
    }

如果想接收返回的头结点,完全可以定义一个show的重载方法:

java 复制代码
  //从传入的head开始遍历
    public void show (Node newhead) {

        Node cur = newhead;

        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }

        System.out.println();
    }

最后调用即可:

java 复制代码
  public static void main (String[]args){
        MyLinkedList x = new MyLinkedList();
      
        x.addFirst(67);
        x.addFirst(24);
        x.addFirst(48);
        x.addFirst(24);

        x.show();
        System.out.println("============");
        Node a = x.reverseList(x.head);
        x.show(a);
}

java 复制代码
public Node partition(Node head, int x) {
            // 1、初始化变量
            Node bs = null;
            Node be = null;
            Node as = null;
            Node ae = null;

            // 2、定义 cur 并初始化
            Node cur = head;

            // 3、遍历链表
            while(cur != null) {
                if(cur.val < x) {
                    // 处理小于 x 的节点
                    if(bs == null) {
                        bs = cur;
                        be = cur;
                    } else {
                        be.next = cur;
                        be = be.next;
                    }
                } else {
                    // 处理大于等于 x 的节点
                    if(as == null) {
                        as = cur;
                        ae = cur;
                    } else {
                        ae.next = cur;
                        ae = ae.next;
                    }
                }
                // 4、移动指针
                cur = cur.next;
            }

            // 5、连接两条链
            if(bs == null) {
                return as;
            }
            be.next = as;

            //6、找到最后结点置空
            if(ae != null){
                ae.next = null;
            }
            return bs;
        }

测试用例:

java 复制代码
    public static void main (String[]args){
        LinkPlus x = new LinkPlus();
        //x.createList();
        x.addFirst(67);
        x.addFirst(24);
        x.addFirst(8888);
        x.addFirst(48);
        x.addFirst(24);

        x.show();

        //没有小于的就返回原链表
        x.show(x.partition(x.head,24));
    }

Ⅱ逻辑图解

⬇️

⬇️



这两道相对简单些😎

3、链表的中间结点
4、返回倒数第k个结点

Ⅰ代码解析

java 复制代码
  //找到链表的中间结点
        public Node middleNode(Node head) {
            Node fast = head;
            Node slow = head;

            //奇偶兼容
            while(fast != null && fast.next != null){
                fast = fast.next.next;
                slow = slow.next;
            }

            return slow;
        }

想要从slow为头向后打印,与上面题目同理

java 复制代码
public static void main (String[]args){
        MyLinkedList x = new MyLinkedList();
           //x.createList();
        x.addFirst(67);
        x.addFirst(24);
        x.addFirst(8888);
        x.addFirst(48);
        x.addFirst(24);

        x.show();

        Node cur = x.middleNode(x.head);
        x.show(cur);       
}

java 复制代码
  //返回倒数第k个结点的值
    public int kthToLast(Node head, int k) {
        Node fast = head;
        Node slow = head;
        int count = 0;

        //
        if(k < 0){
            return -1;
        }

        while(count != k-1){
            fast = fast.next;
            //写在这里就不用在前面写k > size,少一次遍历
            if(fast == null){
                return -1;
            }
            count++;
        }
        while(fast.next != null){

            fast = fast.next;
            slow = slow.next;
        }
        return slow.val;
    }

测试用例:

java 复制代码
 public static void main (String[]args){
        MyLinkedList x = new MyLinkedList();
           //x.createList();
        x.addFirst(67);
        x.addFirst(24);
        x.addFirst(8888);
        x.addFirst(48);
        x.addFirst(24);

        x.show();

        int a = x.kthToLast(x.head,3);
        System.out.println(a);        
}

Ⅱ逻辑图解


本章完

相关推荐
无籽西瓜a2 小时前
【西瓜带你学设计模式 | 第七期 - 适配器模式】适配器模式 —— 类适配器与对象适配器实现、优缺点与适用场景
java·后端·设计模式·软件工程·适配器模式
mjhcsp2 小时前
AT_arc205_c [ARC205C] No Collision Moves 题解
开发语言·c++·算法·题解
Yzzz-F2 小时前
Problem - 1114C - Codeforces[勒让德公式]
算法
Oliver_LaVine2 小时前
idea启动后端项目-控制台中文乱码处理
java·ide
Flittly2 小时前
【SpringAIAlibaba新手村系列】(6)PromptTemplate 提示词模板与变量替换
java·spring boot·agent
yaaakaaang2 小时前
3.springboot,用eclipse轻松创建~
java·spring boot·eclipse
计算机学姐2 小时前
基于SpringBoot的新能源充电桩管理系统
java·vue.js·spring boot·后端·mysql·spring·java-ee
木井巳2 小时前
【笔试强训】Day1
java·算法
风萧萧19992 小时前
Milvus Java 快速入门
java·开发语言·milvus