实现单向链表(假头)和双向链表(假头和假尾)

看清合法与不合法的要求!!!!
在这里插入图片描述

单向链表(别忘了节点类)


//创建链表的节点
public class ListNode {
    int val;
    ListNode next;
    ListNode(int val){ 
        this.val=val; 
    }
}


//链表的尺寸和“假”头结点
class MyLinkedList {

        int size;
        ListNode head;

        public MyLinkedList() {
            size = 0;
            head = new ListNode(0);//这是个假头
        }

        /** 按照序号查找节点的值,没有序号就直接返回-1*/
        public int get(int index) {

            // 无效序号(有效序号还是类似数组的序号,从0开始,到size-1结束)
            if (index < 0 || index >= size){
                return -1;
            }

            ListNode curr = head;//注意这是个假头

            //i < index + 1必须这样,为了越过这个假头
            for(int i = 0; i < index + 1; ++i){
                curr = curr.next;
            }
            return curr.val;
        }

        /** 增加一个头结点,注意仍然位于那个假头的后面 */
        public void addAtHead(int val) {
            addAtIndex(0, val);
        }



        /** 增加一个尾结点 */
        public void addAtTail(int val) {
                addAtIndex(size, val);
        }



        /** 在对应的任意合法位置加入一个节点*/
        public void addAtIndex(int index, int val) {

                //节点的序号超过链表的尺寸就不会添加(原来的链表的有效序号是0--size-1,新加的节点最大序号是size)
                if (index > size) return;

                // [so weird] If index is negative, 
                // the node will be inserted at the head of the list.
                if (index < 0) index = 0;

                ++size;//调节尺寸

                // 寻找要加入位置节点的前一个节点,(如果输入序号就是0,直接加到假头节点的后面)
                ListNode pred = head;
                for(int i = 0; i < index; ++i){
                    pred = pred.next;
                } 

                // 将要加入的节点的下一个==前一个节点的下一个节点(原来这个位置的节点依次顺延往后)
                //前一个节点的下一个节点的下一个节点==带加入的节点
                ListNode toAdd = new ListNode(val);
                toAdd.next = pred.next;
                pred.next = toAdd;
        }



        /** 删除对应位置的节点 */
        public void deleteAtIndex(int index) {
            
            // if the index is invalid, do nothing
            if (index < 0 || index >= size) return;

            size--;

            // 找到待删除节点的前一个节点
            ListNode pred = head;
            for(int i = 0; i < index; ++i) pred = pred.next;

            // 前一个节点的下一个节点指向待删除节点的下一个节点
            pred.next = pred.next.next;
        }
}


/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

双向链表(假头和假尾)

public class ListNode {
  int val;
  ListNode next;
  ListNode prev;
  ListNode(int x) { val = x; }
}

class MyLinkedList {
  int size;
  ListNode head, tail;  // 设置一个假头和一个假尾

  public MyLinkedList() {
    size = 0;//这个size指的是有效节点的个数
    head = new ListNode(0);
    tail = new ListNode(0);
    head.next = tail;//初始化时,假头和假尾互指
    tail.prev = head;
  }


  public int get(int index) {
    // if index is invalid
    if (index < 0 || index >= size) return -1;

    // 注意假头和假尾,以及如何快速寻找序号位置(离头近还是离尾近)
    //index+1和(size-1)-index+1==size-index相互比较--分别需要移动的步数
    //待寻找节点
    ListNode curr = head;

    if (index + 1 < size - index){
        for(int i = 0; i < index + 1; ++i){
            curr = curr.next;
        }
    }else {
        curr = tail;
        for(int i = 0; i < size - index; ++i){
            curr = curr.prev;
        }
    }

    return curr.val;
  }





    public void addAtHead(int val) {

        //待插入节点的前后两个节点
        ListNode pred = head, succ = head.next;

        ++size;

        //拼接两头的节点
        ListNode toAdd = new ListNode(val);
        toAdd.prev = pred;
        toAdd.next = succ;
        pred.next = toAdd;
        succ.prev = toAdd;
    }




    public void addAtTail(int val) {

         //待插入节点的前后两个节点
        ListNode succ = tail, pred = tail.prev;

        ++size;

        ListNode toAdd = new ListNode(val);
        toAdd.prev = pred;
        toAdd.next = succ;
        pred.next = toAdd;
        succ.prev = toAdd;
    }




    public void addAtIndex(int index, int val) {

        if (index > size) return;

        // [so weird] If index is negative, 
        // the node will be inserted at the head of the list.
        if (index < 0) index = 0;

        //寻找待插入节点的前一个节点和后一个节点(哪头可以快速寻找)
        ListNode pred, succ;

        if (index < size - index) {//寻找前一个节点(index-1位置)
            pred = head;
            for(int i = 0; i < index; ++i){
                 pred = pred.next;
            }
            succ = pred.next;

        }else {//注意:寻找到原来这个位置(index位置)的节点是要作为新的这个节点的后一个节点的
            succ = tail;
            for (int i = 0; i < size - index; ++i){
                 succ = succ.prev;
            }
            pred = succ.prev;
        }

        // 拼接三段
        ++size;
        ListNode toAdd = new ListNode(val);
        toAdd.prev = pred;
        toAdd.next = succ;
        pred.next = toAdd;
        succ.prev = toAdd;
    }





    public void deleteAtIndex(int index) {
        // if the index is invalid, do nothing
        if (index < 0 || index >= size) return;

        // 寻找待删除节点的前一个节点和后一个节点(哪头可以快速寻找)
        ListNode pred, succ;

        if (index < size - index) {//寻找前一个节点(index-1位置)
           pred = head;
           for(int i = 0; i < index; ++i) {
               pred = pred.next;
           }
           succ = pred.next.next;

        } else {//寻找后一个节点(index+1位置)
            succ = tail;
            for (int i = 0; i < size - index - 1; ++i){
                succ = succ.prev;
            } 
            pred = succ.prev.prev;
        }

        // 待删除节点的前一个节点与后一个节点互指
        --size;
        pred.next = succ;
        succ.prev = pred;
    }
}


/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值