JAVA中链表的实现


前言

链表是一种物理存储单元上非连续、非顺序,但在逻辑上是有顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的


一、单链表

链表中的数据是以节点来表示的,每个节点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个节点的地址数据。
以“节点的序列”表示线性表称作线性链表(单链表),单链表是链式存取的结构。

存放节点值的数据域存放节点的下一个节点的地址的链域
   data                   |           next                                              
   data                   |           next

以两个节点为例,假设第二个节点得地址是0x111,那么第一个节点的next存储得就是0x111,这样一次链接,就可以形成一个单链表,第一个节点是单链表得头节点用head标识,且最后一个节点得next为null.

二、实现单链表及部分单链表练习

1.先创建一个节点

 class Node {
        public int val;
        public Node next;

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

2.头插法 -addFirst方法的实现

public class MyLinkedList {      //创建单链表
    public Node head = null;

    //头插法
    public void addFirst(int val) {
        Node node = new Node(val);
        node.next = this.head;
        this.head = node;
    }

3.尾插法 -addLast方法的实现

public void addLast(int val) {
        Node node = new Node(val);
        if (this.head == null) {
            this.head = node;
        } else {
            Node cur = this.head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }

4.任意位置插入,第一个数据为0号下标,-addIndex方法的实现

//任意位置插入,第一个数据为0号下标
    public void addIndex(int index, int data) {
        //位置的合法性
        if (index < 0 || index > getlength()) {
            System.out.println("插入的位置不合法!");
            return;
        }
        if (index == 0) {
            addFirst(data);   //头插法
            return;
        }
        if (index == getlength()) {
            addLast(data);       //尾插法
            return;
        }
        //中间插入
        Node ret = searchIndexSubOne(index);
        //ret 存储的是index-1位置的节点的
        Node node = new Node(data);
        node.next = ret.next;
        ret.next = node;
    }

其中searchIndexSubOne(index)方法为找到要插入节点的前一个节点,具体的实现:

public Node searchIndexSubOne(int index) {
        Node cur = this.head;
        while (index - 1 != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

5.获取链表长度-getLength方法的实现

public int getlength() {
        int len = 0;
        Node cur = this.head;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        return len;
    }

6.判断单链表中是否有某个元素-contains方法的实现

 public boolean contains(int key) {
        Node cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

7.删除第一次data/value为key的节点-remove方法的实现

public void remove(int key) {
        if (this.head.val == key) {
            this.head = this.head.next;
            return;
        }
        //先找到前驱节点
        Node pre = searchPrev(key);
        if (pre == null) {
            System.out.println("没有找到要删除的元素");
        } else {
            Node del = pre.next;
            pre.next = del.next;
        }
    }

其中 searchPrev(key)方法是为了找到前驱节点,即要删除的前一个节点,具体的实现:

public Node searchPrev(int key) {
        Node cur = this.head;
        while (cur != null) {
            while (cur.next.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

8.删除所有data/value为key的节点-removeAllKey方法的实现

 public void removeAllKey(int key) {
        Node cur = this.head.next;
        Node pre = this.head;
        while (cur != null) {
            if (cur.val == key) {
                pre.next = cur.next;
                cur = cur.next;
            } else {
                pre = cur;
                cur = cur.next;
            }
        }
        if (this.head.val == key) {
            this.head = this.head.next;
        }
    }

9.单链表show方法的实现

public void show() {
        Node cur = this.head;
        while (cur != null) {
            System.out.print(cur.val + "  ");
            cur = cur.next;
        }
    }

10.翻转一个单链表(头插法)–reverseList方法的实现

 public Node reverseList() {
        if (this.head == null && this.head.next == null) {
            return null;
        }
        Node cur = this.head;
        Node curNext = this.head.next;
        cur.next = null;//  如果没有将第一个next清除就会变成循环
        cur = curNext;
        while (cur != null) {
            curNext = curNext.next;
            cur.next = this.head;
            this.head = cur;
            cur = curNext;
        }
        return this.head;
    }

11.找到单链表的中间节点–middleNode方法的实现

//快慢指针法 来找中间节点
public int middleNode() {
    if (this.head == null) {
        return this.head.val;
    }
    Node fast = this.head;
    Node slow = this.head;
    while (fast != null && fast.next != null) {  //用&&来区分奇偶情况,满足其中一个情况就结束;
        // 偶节点fast=null ;奇节点fast.next=null停止
        fast = fast.next.next;
        slow = slow.next;
    }
    return slow.val;
}

12.找到链表开始入环的第一个节点–ringEntrance方法的实现

public Node ringEntrance() {
        Node fast = this.head;
        Node slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }
        if (fast == null || fast.next == null) {
            return null;
        }
        slow = this.head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

13.给定一个链表,判断链表中是否有环–hasCycle方法的实现

public boolean hasCycle() {
        Node fast = this.head;
        Node slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        if (fast == null || fast.next == null) {
            return false;
        }
        return true;
    }

13. 链表的回文结构 --chkPalindrome方法的实现

public boolean chkPalindrome() {
        if (this.head == null) {
            return false;
        }
        if (this.head.next == null) {
            return true;
        }
        Node fast = this.head;
        Node slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //翻转从中间节点起后面的节点  三个变量
        Node cur = slow.next;
        while (cur != null) {
            Node curNext = slow.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        while (this.head != slow) {
            //奇数情况
            if (this.head.val != slow.val) {
                return false;
            }
            //偶数情况
            if (this.head.next == slow) {
                return true;
            }
            this.head = this.head.next;
            slow = slow.next;
        }
        return true;
    }

14.删除链表重复节点–deleteDuplication方法的实现

 public Node deleteDuplication() {
        Node cur = this.head;
        Node tmpHead = new Node(-1);
        Node newHead = tmpHead;
        while (cur != null) {
            if (cur.next != null && cur.val == cur.next.val) {
                while (cur.next != null && cur.val == cur.next.val) {
                    cur = cur.next;
                }
                cur = cur.next;
            } else {
                tmpHead.next = cur;
                tmpHead = tmpHead.next;
                cur = cur.next;
            }
        }
        tmpHead.next = null;
        return newHead.next;
    }

15. 链表分割,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前–partition方法的实现

public Node partition(int x) {
        Node cur = this.head;
        Node bs = null;                       //将节点分为两部分大于x 和小于等于x
        Node be = null;      // bs表示大于x节点的头部(开始)  be表示大于x节点的尾部
        Node as = null;       //as表示小于等于x节点的头部(开始)  ae表示小于等于x节点的尾部
        Node ae = null;
        while (cur != null) {
            if (cur.val < x) {     //大于x
                if (bs == null) {
                    bs = cur;
                    be = bs;
                } else {
                    be.next = cur;
                    be = be.next;
                }
                //小于等于x
            } else {
                if (as == null) {
                    as = cur;
                    ae = as;
                } else {
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
        if (bs != null) {
            this.head = bs;
            be.next = as;
        } else {
            this.head = as;
        }
        if (ae != null) {
            ae.next = null;
        }
        return this.head;
    }

16.输入一个链表,输出该链表中倒数第k个结点–FindKthToTail方法的实现

思路:快慢指针 快指针先走k-1步 ;慢指针再走,就会相差k-1步,相差k个节点,当快指针走到最后一个节点是,慢指针刚好到k;

public Node FindKthToTail(int k) {
        if (head == null) {
            return null;
        }
        if (k <= 0) {
            return null;
        }
        Node fast = this.head;
        Node slow = this.head;
        while (k - 1 != 0) {
            if (fast.next != null) {   //这注意!!防止空指针
                fast = fast.next;
                k--;
            } else {
                System.out.println("给的k值太大");
                return null;
            }
            while (fast.next != null) {
                fast = fast.next;
                slow = slow.next;
            }
        }
        return slow;
    }
}

总结

链表的问题需要逻辑严谨,不断地修改调试,代码太多debug找错点也很困难,需要反复实践,找到最快的断点来找出bug

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值