Java之单链表问题

单链表问题

1.单链表及节点的定义

class Node{
    public int data;
    public Node next;

    public Node(){
    }
    public Node(int data) {
        this.data = data;
    }
}
class LinkedList {
    public Node head;
    //在此实现单链表的插入,删除等方法
}

2.单链表的头插法

  //头插法
    public void addFirst(int data) {
        Node node = new Node(data);
        //第一次插入,单链表为空
        if (this.head == null) {
            this.head = node;
        } else {
            //单链表不为空
            node.next = this.head;
            this.head = node;
        }
    }

3.单链表的尾插法

    //尾插法
    public void addLast(int data) {
        Node node = new Node(data);
        if (this.head == null) {
            this.head = node;
        } else {
            Node cur = this.head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }

4. 单链表-从指定位置插入

这里用到了几个方法,获取单链表长度下标的合法性检查找index-1位置节点

    //从指定位置插入
    public boolean addIndex(int index, int data) {
        //1.先检查下标的合法性
        checkIndex(index);
        // 若下表为0,则采用头插法
        if (index == 0) {
            addFirst(data);
        }
        //找到index-1 位置的节点,作为要插入位置的前趋
        Node cur = searchIndex(index);
        Node node = new Node(data);
        node.next = cur.next;
        cur.next = node;
        return true;
    }
    
        //检查下表合法性
    private void checkIndex(int index) {
        if (index < 0 || index > getLength()) {
            throw new IndexOutOfBoundsException("下标不合法");
        }
    }
    
        //获取链表长度
    private int getLength() {
        int count = 0;
        Node cur = this.head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
    
      //寻找index-1位置的节点
    private Node searchIndex(int index) {
        int count = 0;
        Node cur = this.head;
        while (count < index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

5.删除第一次出现关键字为key的节点

单链表的节点删除,需要找到前趋节点进行绑定,在此方法中实现了寻找前趋节点的方法。

  //删除第一次出现关键字为key的节点
    public void remove(int key) {
        if (this.head.data == key) {
            this.head = this.head.next;
            return;
        }
        Node prev = searchPrev(key);
        if (prev == null) {
            return;
        }
        Node del = prev.next;
        prev.next = del.next;
    }

    //寻找前趋节点
    private Node searchPrev(int key) {
        Node prev = this.head;
        while (prev.next != null) {
            if (prev.next.data == key) {
                return prev;
            }
            prev = prev.next;
        }
        return null;
    }

6.删除所有关键字为key的节点

遍历单链表,把所有值为key 的节点删除

    //删除所有关键字为key的节点
    public void removeAllKey(int key) {
        if (this.head.data == key) {
            this.head = this.head.next;
        }
        Node prev = this.head;
        Node cur = this.head.next;
        while (cur != null) {
            if (prev.next.data == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
    }

7.单链表的打印

   //打印链表
    public void display() {
        Node cur = this.head;
        while (cur != null) {
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }

8.单链表的清空

 //清空单链表
    public void clear() {
        this.head = null;
    }

9.单链表的逆置/反转

  //单链表的逆置,反转
    public Node reverseList() {
        Node prev = null;//当前需反转节点的前趋
        Node cur = this.head;//当前需反转的节点
        Node newHead = null;// 新的单链表的头结点
        while (cur != null) {
            Node curNext = cur.next;
            if (curNext == null) {
                newHead = cur;
            }
            cur.next = prev;
            prev = cur;
            cur = curNext;
        }
        return newHead;
    }

    //反转完之后需重写打印函数
    public void display2(Node newHead) {
        Node cur = newHead;
        while (cur != null) {
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }

10.单链表-找中间节点

返回中间节点;
定义快指针fast,慢指针slow,fast走两步,slow走一步
当fast为null,或者fast的next为null时,slow所指刚好为中间节点

    public Node middleNode() {
        Node fast = this.head;
        Node slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

11.单链表-找倒数第K个节点

找倒数第k个节点:
先让fast走k-1步,
再让fast、slow一起走,
当fast下一个无节点时,slow刚好指向倒数第k个节点

public Node findKthToTail(int k) {
        Node fast = this.head;
        Node slow = this.head;
        while (k - 1 > 0) {
            if (fast.next != null) {
                fast = fast.next;
                k--;
            } else {
                System.out.println("没有这个节点!");
                return null;
            }
        }
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

12.以基准重新排列链表

以基准重新排列链表
方法:基准前的比基准小->产生一个新的顺序表,基准后的比基准大->产生一个顺序表;
将两个顺序表拼接起来
.

 public Node partition(int x) {
        Node beforeStart = null;
        Node beforeEnd = null;
        Node afterStart = null;
        Node afterEnd = null;
        Node cur = this.head;
        while (cur != null) {
            Node curNext = cur.next;
            cur.next = null;
            if (cur.data < x) {
                if (beforeStart == null) {
                    beforeStart = cur;
                    beforeEnd = cur;
                } else {
                    beforeEnd.next = cur;
                    beforeEnd = cur;
                }
            } else {
                if (afterStart == null) {
                    afterStart = cur;
                    afterEnd = cur;
                } else {
                    afterEnd.next = cur;
                    afterEnd = cur;
                }
            }
            cur = curNext;
        }
        //如果第一个部分没有数据,也就是没有比基准小的数据
        if (beforeStart == null) {
            return afterStart;
        }
        beforeEnd.next = afterStart;
        return beforeStart;
    }

13.删除单链表中重复节点

    //删除重复节点
    public Node deleteDuplication() {
        Node newHead = new Node(-1);
        Node tmp = newHead;
        Node cur = this.head;
        while (cur != null) {
            //找到相同的节点
            if (cur.next != null && cur.data == cur.next.data) {
                while (cur.next != null && cur.data == cur.next.data) {
                    cur = cur.next;
                }
                cur = cur.next;
                tmp.next = cur;
            } else {
                //没找到相同的节点
                tmp.next = cur;
                tmp = tmp.next;
                cur = cur.next;
            }
        }
        return newHead.next;
    }

14.单链表回文结构判断

回文结构:
定义快慢指针,让fast走两步,slow走一步,fast为空或者fast的下一个节点为空时,slow刚好在中间位置;
反转后半部分,从中间的下一个反转,1->2->3->2->1;
定义p指向中间的下一个,完成反转 1->2->3<-2<-1;
遍历单链表,head 从前面走,slow从尾巴走,直到相遇前对应位置元素均相等,则是回文结构

  public boolean clkPalindrome() {
        //单链表为空,不是回文结构
        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 p = slow.next;
        while (p != null) {
            Node pNext = p.next;
            p.next = slow;
            slow = p;
            p = pNext;
            if (p != null) {
                pNext = p.next;
            }
        }
        while (head != slow) {
            if (this.head.next == slow) {
                return true;
            }
            if (head.data != slow.data) {
                return false;
            }
            head = head.next;
            slow = slow.next;
        }
        return true;
    }

15.判断单链表是否有环

我们可以自己创造一个环,来判断方法的正确性

  //创造一个环
    public void createCycle() {
        Node cur = this.head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = this.head.next.next;
    }

    //判断链表是否有环
    //一个指针走一步,一个指针走两步,若有环,能在最短时间内尽早相遇
    //若一个走三步,一个走两步,有可能会跳过,很久才会相遇或者永远不会相遇。
    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;
            }
        }
        return false;
    }

16.找到环的入口节点

找到环的入口节点:
从头开始走,fast 走两步,slow 走一步,找到第一次相遇的点;
第一次相遇后,将一个拉到单链表的头,这里将fast 拉到头,slow不动;
让fast、slow都一步一步走,再次相遇就为环的入口点

   public Node detectCycle(Node node) {
        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;
        }
        fast = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值