链表CODE:链表的实现

链表实现的技巧:

  1. 理解指针或引用的含义
    将变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到变量。java与指针对应的是引用
  2. 警惕指针丢失和内存泄漏
    指针操作的时候一定要注意顺序,比如:
    在这里插入图片描述
    p->next = x;  // 将 p 的 next 指针指向 x 结点;
    x->next = p->next;  // 将 x 的结点的 next 指针指向 b 结点;
    
    P的下一个结点指向X
    这时X将他的下一个结点指向P的下一个结点的时候(由于P的下一个已经指向X了),这会就是自己指向自己了,因此链表断裂成凉拌
  3. 删除链表结点时,记得手动释放内存空间(java无视)
  4. 利用哨兵简化开发(下面有代码示例)
    如果引入哨兵,不管任何时候,不管链表是否是空,head指针一定会指向这个哨兵结点,因为每次删除和添加的时候,不用判断链表是否为空等边界情况,简化了代码。
    • 有哨兵结点的链表叫做带头链表
    • 没有哨兵结点的链表叫做不带头链表


/**
 * 链表的结点
 */
public class LinkNode {
    public int val;
    public LinkNode next;

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

/**
 * 单链表的实现
 */
public class MyLinkedList {
    private LinkNode head;
    private int length;

    public MyLinkedList() {
        //创建一个哨兵,简化代码
        head = new LinkNode(-1);
        length = 0;
    }

    /**
     * 获取链表中第 index 个节点的值。如果索引无效,则返回-1。
     *
     * @return
     */
    public int get(int index) {
        if (index < 0 || index >= length) {
            return -1;
        }
        LinkNode node = head;

        for (int i = 0; i <= index; i++) {
            node = node.next;
        }
        return node.val;
    }

    /**
     * 在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
     *
     * @param val
     */
    public void addAtHead(int val) {
        LinkNode node = new LinkNode(val);
        //这块虽然head有初始值,但是每次取值是从head.next值开始取的,所以头哨兵在取值的时候忽略了
        node.next = head.next;
        //始终将哨兵结点放在第一位
        head.next = node;
        length++;
    }

    /**
     * 将值为 val 的节点追加到链表的最后一个元素。
     *
     * @param val
     */
    public void addAtTail(int val) {
        LinkNode node = new LinkNode(val);
        LinkNode curr = head;

        while (curr.next != null){
            curr = curr.next;
        }
        curr.next = node;
        length++;
    }

    /**
     * 在链表中的第 index 个节点之前添加值为 val  的节点。
     * 如果 index 等于链表的长度,则该节点将附加到链表的末尾。
     * 如果 index 大于链表长度,则不会插入节点。
     * 如果index小于0,则在头部插入节点。
     *
     * @param index
     * @param val
     */
    public void addAtIndex(int index, int val) {
        if (index <= 0) {
            addAtHead(val);
            return ;
        }
        if (index > length) {
            return;
        }
        if (index == length) {
            addAtTail(val);
            return ;
        }
        LinkNode curr = head;
        //计算index结点的前一个结点,i < index ->到循环终止的时候取的是index前一个数据
        for (int i = 0; i < index; i++) {
            //因为循环条件i<index,取得已经是index前一个数据了,但是因为有头哨兵的存在,所以要从head.next开始算
            curr = curr.next;

            //而之前用的的i == index 条件,是直接定位到index结点了。这点谨记
//            if(i == index){
//                node.next = curr;
//                last.next = node;
//            }
        }
        LinkNode node = new LinkNode(val);
        node.next = curr.next;
        curr.next = node;
        length++;
    }

    /**
     * 如果索引 index 有效,则删除链表中的第 index 个节点。
     *
     * @param index
     */
    public void deleteAtIndex(int index) {

        if (index < 0 || index >= length) {
            return;
        }
        LinkNode curr = head;
        for (int i = 0; i < index; i++) {
            curr = curr.next;
        }
        curr.next = curr.next.next;
        length--;
    }

    public void printLnik() {
        LinkNode curr = head;
        for (int i = 0; i < length; i++) {
            System.out.print(curr.next.val + "         ");
            curr = curr.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {

        MyLinkedList linkList = new MyLinkedList();

        linkList.addAtIndex(0, 1);
        linkList.printLnik();

        System.out.println(linkList.get(0));


        System.out.println(linkList.get(1));


    }
    /**
     * 判断链表中是否有环
     * @param head
     * @return
     */
    public boolean hasCycle(LinkNode head) {
        LinkNode curr = head;
        HashSet<Object> hashSet = new HashSet<>();
        while (curr.next != null){
            if(hashSet.contains(curr)){
                return true;
            }
            hashSet.add(curr);
            curr = curr.next;
        }
        return false;
    }
    /**
     * 反转链表
     * @param head
     * @return
     */
    public LinkNode reverseList(LinkNode head) {
        LinkNode prev = null; //翻转后新的链表
        LinkNode curr = head; //当前链表
        while (curr != null){
            LinkNode tem = curr.next; //将链表除头结点之外的其他结点赋值给tem指针
            curr.next = prev;  //将当前链表的头结点和prev链表结合(翻转后的链表)
            prev = curr;       //然后将结合后的新链表赋值给prev链表(翻转后的链表)
            curr = tem;        //将tem指针赋值给当前链表进行下一轮翻转(tem指针链表到这已经少了第一个结点)
        }
        return prev;
    }
    /**
     * 删除链表中倒数第n个结点
     * @param head
     * @param n
     * @return
     */
    public LinkNode removeNthFromEnd(LinkNode head, int n) {
        LinkNode fast = head;
        LinkNode slow = head;
        //因为是带头链表,所以 不存在为空的情况,并且因为有头结点,所以这块是n
        //思路利用快慢指针
        for (int i = 0; i < n ; i++) {
            fast = fast.next;
        }
        while (fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return head;

    }
     /**
     * 链表的中心结点
     * 如果有两个中间结点,则返回第二个中间结点。
     * @param head
     * @return
     */
    public LinkNode middleNode(LinkNode head) {
        LinkNode fast = head;
        LinkNode slow = head;


        while (fast.next != null){

            fast = fast.next.next;
            slow = slow.next;
        }
        //因为链表是带头链表,所以所下一个
        return slow.next;
    }
     /**
     * 判断链表中是否有环(利用快慢指针)
     * 若链表中存在环,则快指针一定会追上慢指针
     * @param head
     * @return
     */
    public boolean hasCycleF(LinkNode head) {
        LinkNode fast = head;
        LinkNode curr = head;
        while (fast.next != null){
            if(fast == curr)
                return true;
            fast = fast.next;
            curr = curr.next;

        }
        return false;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值