2021-6-13-剑指笔记01

笔记01

_11_返回倒数第k个节点

package LeetCode._面试经典.笔记01;

public class _11_返回倒数第k个节点 {

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

    /*
    1.递归:代码简洁但是需要占用O(N)的空间
    2.迭代:代码稍微复杂一点但是效率快
    * */
    public int K=0;
    public int value=0;
    public int kthToLast(ListNode head, int k) {
       K=k;
       nthToLast(head);
       return value;
    }
    //递归每次返回该节点的下标(tip:最后1个为倒数第1个)
    public int nthToLast(ListNode node){
        if (node==null) return 0;
        int l=nthToLast(node.next)+1;
        if (l==K) value=node.val;
        return l;
    }

    //快慢指针
    public int kthToLast02(ListNode head, int k) {
        if (head==null) return 0;
        ListNode slow=head;
        ListNode fast=head;
        while (k>0&&fast!=null){//最后fast肯定到null所以需要多走1步
            fast=fast.next;
            k--;
        }
        if (k!=0) return 0;//不合法的k
        while (fast!=null){
            slow=slow.next;
            fast=fast.next;
        }
        return slow.val;
    }

}

_12_删除链表中间节点

package LeetCode._面试经典.笔记01;

import org.junit.Test;

import java.util.Random;

public class _12_删除链表中间节点 {

    public class ListNode {
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
    }
    /*
    中间节点:除去首尾节点外都叫中间节点,只能访问删除的的节点
    tips:若待删除的是尾结点,该题无解,面试的时候应该给面试官指出
    将其为null ? 很明显这就是对指针的不理解了,你置null只是将node置null,却并未将前驱节点的next置null,其还是只想该块内存额
    * */

    public void deleteNode(ListNode node) {
        if (node==null) return;
        if (node.next==null) {
            System.out.println("尾结点:"+node.val);
            node.val=-1;
            return;
        }
        node.val=node.next.val;
        node.next=node.next.next;
    }

    @Test
    public void test(){
        //[a,b] random(b-a)+a
        ListNode head=new ListNode(1);
        ListNode n2=new ListNode(2);
        ListNode n3=new ListNode(3);
        ListNode n4=new ListNode(4);
        head.next=n2;
        n2.next=n3;
        n3.next=n4;
        deleteNode(n4);
        print(head);
    }
    public void print(ListNode node){
        ListNode p=node;
        while (p!=null){
            System.out.println(p.val);
            p=p.next;
        }
    }
}

_13_分割链表

package LeetCode._面试经典.笔记01;

import org.junit.Test;

public class _13_分割链表 {
    public class ListNode {
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
    }

    /*
    询问如果不考虑"稳定性":原来的顺序不变-->1个链表:头插+尾插
    考虑:2个链表
    * */
    public ListNode partition01(ListNode head, int x) {
       if (head==null) return null;
       ListNode lowS=new ListNode(-1);
       ListNode lowL=lowS;

       ListNode highS=new ListNode(-1);
       ListNode highL=highS;

       ListNode p=head;
       while (p!=null){//保证losS或highS最后1个不为null
           if (p.val<x){
               lowL.next=p;
               lowL=lowL.next;
           }
           else {
               highL.next=p;
               highL=highL.next;
           }
           p=p.next;
       }
       //避免出现环
       lowL.next=null;
       highL.next=null;
       lowL.next=highS.next;
       return lowS.next;
    }

    //不考虑稳定性,利用现有节点进行头插尾插法,更简洁代码
    //左边部分<x 右边部分>=x
    public ListNode partition(ListNode head, int x){
        if (head==null) return null;
        ListNode S=head;
        ListNode L=head;
        ListNode P=head.next;
        while (P!=null){
            ListNode next=P.next;
            if (P.val<x){
                P.next=S;//p.next断开,下面就用不到了
                S=P;
            }
            else {
                L.next=P;
                L=P;
            }
            P=next;
        }
        L.next=null;
        return  S;
    }

    @Test
    public void  test(){
        ListNode n1=new ListNode(1);
        ListNode n2=new ListNode(4);
        ListNode n3=new ListNode(3);
        ListNode n4=new ListNode(2);
        ListNode n5=new ListNode(5);
        ListNode n6=new ListNode(2);
        n1.next=n2;
        n2.next=n3;
        n3.next=n4;
        n4.next=n5;
        n5.next=n6;

        ListNode res=partition(n1,3);
    }

}

_14_链表求和

package LeetCode._面试经典.笔记01;

public class _14_链表求和 {
     public class ListNode {
           int val;
           ListNode next;
           ListNode(int x) { val = x; }
     }
    /*
    首先回顾一下加法:
       6 1 7
    +  2 5 9

    可用递归实现,注意短链表的空指针
    * */
    public ListNode addTwoNumbers01(ListNode l1, ListNode l2) {
        return nextList(l1,l2,0);
    }
    public ListNode nextList(ListNode l1,ListNode l2,int carry){
        if (l1==null&&l2==null&&carry==0) return null;
        int sum=carry;
        if (l1!=null) sum+=l1.val;
        if (l2!=null) sum+=l2.val;
        ListNode res=new ListNode(sum%10);//先序遍历
        res.next=nextList(l1==null?null:l1.next,l2==null?null:l2.next,sum/10);
        return res;
    }

    /*
    进阶:整数正向存储
    因为需要配对,所以需要先补0
    * */

    public class PartialSum{//封装类
        public ListNode sum=null;
        public int carry=0;
    }

    //两个节点相加得到一个封装加数节点PartialSum,再往前传递即可
    PartialSum addHelper(ListNode l1,ListNode l2){
        if (l1==null&&l2==null){
            return new PartialSum();
        }
        PartialSum sumHelper=addHelper(l1.next,l2.next);
        //得到l1.next,l2.next的加数类,直接拿加数类的节点即可
        //构造当前加数类的节点
        int val=sumHelper.carry+l1.val+l2.val;
        ListNode node=new ListNode(val%10);//当前加数节点
        if (sumHelper.sum!=null)
            node.next=sumHelper.sum;
        sumHelper.carry=val/10;
        return sumHelper;//最后返回的是一个加数节点和进位
    }


    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1==null||l2==null) return l1==null?l2:l1;
        int count1=0;
        ListNode p1=l1;
        while (p1!=null){
            count1++;
            p1= p1.next;
        }
        int count2=0;
        ListNode p2=l1;
        while (p2!=null){
            count2++;
            p2= p2.next;
        }
        if (count1<count2) return addTwoNumbers(l2,l2);
        //保证l1最长
        int diff=count1-count2;
        ListNode head2=new ListNode(0);
        ListNode p=head2;
        for (int i=1;i<=diff;i++){
            ListNode node=new ListNode(0);
            p.next=node;
            p=node;
        }
        if (diff!=0){
            p.next=l2;
            l2=head2;
        }
        PartialSum res=addHelper(l1,l2);
        if (res.carry==0)
             return res.sum;
        else {
            ListNode node=new ListNode(res.carry);//当前加数节点
            if (res.sum!=null)
                node.next=res.sum;
            return node;
        }
    }




}

_15_回文链表

编写一个函数,检查输入的链表是否是回文的。

示例 1:

输入: 1->2
输出: false
示例 2:

输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

package LeetCode._面试经典.笔记01;

import org.junit.Test;

import java.util.ArrayDeque;

public class _15_回文链表 {

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

        //方法1:先将链表反转(头插),然后比较前半部分   O(N)  O(N)
        public boolean isPalindrome01(ListNode head) {
              if (head==null) return true;
              ListNode node=getReverse(head);
              return isReverse(head,node);
        }
        public ListNode getReverse(ListNode head){
            ListNode p=head;
            ListNode r=null;
            while (p!=null){
                ListNode node=new ListNode(p.val);
                node.next=r;
                r=node;
                p=p.next;
            }
            //最后一个节点为头节点
            return r;

        }
        public boolean isReverse(ListNode l1,ListNode l2){
            ListNode p1=l1;
            ListNode p2=l2;
            while (p1!=null&&p2!=null){
                if (p1.val!=p2.val) return false;
                p1=p1.next;
                p2=p2.next;
            }
            return true;
        }

        //---------------------------------------------------------------
        //方法2:迭代法,利用栈+快慢指针  O(N)  O(N)
        public boolean isPalindrome02(ListNode head) {
            if (head==null) return true;

            ListNode slow=head;
            ListNode fast=head;
            ArrayDeque<Integer> deque = new ArrayDeque<>();
            while (fast!=null&&fast.next!=null){
                deque.addFirst(slow.val);
                slow=slow.next;
                fast=fast.next.next;
            }
            //因为有奇数个节点,所以跳过中间节点
            if (fast!=null) slow=slow.next;
            while (slow!=null){
                if (deque.removeFirst()!=slow.val) return false;
                slow=slow.next;
            }
            return true;
        }

       //---------------------------------------------------------------
       //方法3:递归,使用一个封装类
       class Result{
            ListNode node;
            boolean result;

           public Result(ListNode node, boolean result) {
               this.node = node;
               this.result = result;
           }
       }
       public boolean isPalindrome(ListNode head){
          int len=0;
          ListNode p=head;
          while (p!=null){
              len++;
              p=p.next;
          }
           Result reverse = isReverse(head, len);
          return  reverse.result;
       }
       //封装类的意思是返回 内序列的比对结果以及下次比较的内序列的最后1个节点
       Result isReverse(ListNode head,int len){

            if (head==null||len<=0){//偶数个节点
                return new Result(head,true);
            }
            else if (len==1){//奇数个节点
                return new Result(head.next,true);
            }

            Result res=isReverse(head.next,len-2);
            if (!res.result||res.node==null) return res;//向上传递失败信息
            res.result=(head.val==res.node.val);
            res.node=res.node.next;
            return res;

       }


        @Test
        public void test(){

            ListNode n1=new ListNode(1);
            ListNode n2=new ListNode(1);
            ListNode n3=new ListNode(2);
            ListNode n4=new ListNode(1);
            n1.next=n2;
            n2.next=n3;
            n3.next=n4;
            boolean b = isPalindrome(n1);
            System.out.println(b);


        }


}

_16_链表相交

给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。换句话说,如果一个链表的第k个节点与另一个链表的第j个节点是同一节点(引用完全相同),则这两个链表相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

如果两个链表没有交点,返回 null 。
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

package LeetCode._面试经典.笔记01;

import org.junit.Test;

public class _16_链表相交 {
    
      public class ListNode {
          int val;
          ListNode next;
          ListNode(int x) {
              val = x;
              next = null;
          }
     }

     /*
     快慢指针:
     1.长度差,若俩链尾结点不同,直接返回false
     2.  1 2 3 4
             1 2
        长的先多走2步
     * */
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         if (headA==null||headB==null) return null;
         int lenA=0;
         int lenB=0;
         ListNode A=headA;
         ListNode B=headB;
         while (A.next!=null){
             lenA++;
             A=A.next;
         }
         //A现在是尾结点
         lenA+=1;
         while (B.next!=null){
             lenB++;
             B=B.next;
         }
         lenB+=1;
         if (A!=B) return null;
         //第1条链比较长
         if (lenA<lenB) return getIntersectionNode(headB,headA);//别漏了return
         A=headA;
         for (int i=1;i<=lenA-lenB;i++) A=A.next;
         B=headB;
         while (A!=null&&B!=null){
             if (A==B) return A;
             A=A.next;
             B=B.next;
         }
         return null;
    }

    @Test
    public void test(){
        ListNode n1=new ListNode(4);
        ListNode n2=new ListNode(1);
        ListNode n3=new ListNode(8);
        ListNode n4=new ListNode(4);
        ListNode n5=new ListNode(5);
        ListNode n21=new ListNode(5);
        ListNode n22=new ListNode(0);
        ListNode n23=new ListNode(1);
        n1.next=n2;
        n2.next=n3;
        n3.next=n4;
        n4.next=n5;
        n21.next=n22;
        n22.next=n23;
        n23.next=n3;
        ListNode res = getIntersectionNode(n1, n21);
        System.out.println(res.val);

    }
    
    
}

_17_环路检测

给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

package LeetCode._面试经典.笔记01;

public class _17_环路检测 {
    class ListNode {
        int val;
        ListNode next;
        ListNode(int x) {
            val = x;
            next = null;
        }
    }

    /*
    两倍指针+数学规律;
    先找到碰撞点(判断有无环)+同步走;
    推导:2k-k=l = k
         k-x
         l-(k-x)=k-(k-x)=x
    * */
    public ListNode detectCycle(ListNode head) {
        if (head==null) return null;

        ListNode slow=head;
        ListNode fast=head;
        while (fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if (slow==fast) break;
        }
        //slow.next==null是为了防止当个结点不进上面while循环
        if (fast==null||fast.next==null) return null;//无环
        slow=head;
        while (slow!=fast){
            slow=slow.next;
            fast=fast.next;
        }
        return fast;
    }

}

_18_用1个数组实现3栈

三合一。描述如何只用一个数组来实现三个栈。

你应该实现push(stackNum, value)、pop(stackNum)、isEmpty(stackNum)、peek(stackNum)方法。stackNum表示栈下标,value表示压入的值。

构造函数会传入一个stackSize参数,代表每个栈的大小。

示例1:

输入:
[“TripleInOne”, “push”, “push”, “pop”, “pop”, “pop”, “isEmpty”]
[[1], [0, 1], [0, 2], [0], [0], [0], [0]]
输出:
[null, null, null, 1, -1, -1, true]
说明:当栈为空时pop, peek返回-1,当栈满时push不压入元素。
示例2:

输入:
[“TripleInOne”, “push”, “push”, “push”, “pop”, “pop”, “pop”, “peek”]
[[2], [0, 1], [0, 2], [0, 3], [0], [0], [0], [0]]
输出:
[null, null, null, null, 2, 1, -1, -1]

package LeetCode._面试经典.笔记01;

public class _18_用1个数组实现3{

    private final int numOfStack=3;//有几个栈
    private int capacityOfStack;//每个栈的容量
    private int[] values;//分配的总数组
    private int[] size;//每个栈的大小

    public _18_用1个数组实现3(int stackSize) {
          capacityOfStack=stackSize;
          values=new int[numOfStack*capacityOfStack];
          size=new int[numOfStack];
    }
    /*
    首先需要实现2个额外方法
    1.取得栈顶元素的下标
    2.判断栈已满(毕竟数组长度固定)
    * */
    public boolean isFull(int stackNum){
        return size[stackNum]==capacityOfStack;
    }
    public int indexOfTop(int stackNum){
        //基址+偏移量
        return stackNum*capacityOfStack+size[stackNum]-1;
    }

    public void push(int stackNum, int value) {
        if (!isFull(stackNum)){
            values[indexOfTop(stackNum)+1]=value;
            size[stackNum]++;
        }
    }

    public int pop(int stackNum) {
        if (!isEmpty(stackNum)){
            int top=values[indexOfTop(stackNum)];
            size[stackNum]--;
            return top;
        }
        return -1;
    }

    public int peek(int stackNum) {
        if (!isEmpty(stackNum)){
            return values[indexOfTop(stackNum)];
        }
        return -1;
    }

    public boolean isEmpty(int stackNum) {
        return size[stackNum]==0;
    }
}

_19_栈的最小值

请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

package LeetCode._面试经典.笔记01;

import java.util.ArrayDeque;
import java.util.Stack;

public class _19_栈的最小值 {

    //使用链表来实现1个栈
    public class Node{
        int data;
        Node next;
        Node(){}
        Node(int data){this.data=data;}
        Node(int data,Node next){this.data=data;this.next=next;}
    }
    public class myStack{
        Node Top=null;
        //实现4个方法:isEmpty,peek,push,pop
        public boolean isEmpty(){
            return Top==null;
        }
        public int peek(){
            if (Top==null) return -1;
            return Top.data;
        }
        public void push(int data){//栈顶元素为链表头元素,头插法
            Node node = new Node(data);
            node.next=Top;
            Top=node;
        }
        public int pop(){
            if (Top==null) return -1;
            int value=Top.data;
            Top=Top.next;
            return value;
        }
    }

    myStack elem;
    myStack min;

    public _19_栈的最小值() {
       elem=new myStack();
       min=new myStack();
    }

    public void push(int x) {
        elem.push(x);
        //min.isEmpty()犯错点,没判断空
        if (min.isEmpty()||x<=min.peek()) min.push(x);
    }

    public void pop() {
       // new Stack<Integer>().peek();犯错点,如果调用库函数的话,这里要用equals或者先强转
        if (elem.peek()==min.peek())
            min.pop();
        elem.pop();
    }

    public int top() {
       return elem.peek();
    }

    public int getMin() {
        return min.peek();
    }
}

_20_堆盘子

堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。请实现数据结构SetOfStacks,模拟这种行为。SetOfStacks应该由多个栈组成,并且在前一个栈填满时新建一个栈。此外,SetOfStacks.push()和SetOfStacks.pop()应该与普通栈的操作方法相同(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。 进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作。

当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,pop,popAt 应返回 -1.

示例1:

输入:
[“StackOfPlates”, “push”, “push”, “popAt”, “pop”, “pop”]
[[1], [1], [2], [1], [], []]
输出:
[null, null, null, 2, 1, -1]
示例2:

输入:
[“StackOfPlates”, “push”, “push”, “push”, “popAt”, “popAt”, “popAt”]
[[2], [1], [2], [3], [0], [0], [0]]
输出:
[null, null, null, null, 2, 1, 3]

package LeetCode._面试经典.笔记01;


import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class _20_堆盘子 {
    int maxCaps;//每个栈的最大容量
    List<Stack<Integer>> list;//用链表存储栈,增删更快

    public _20_堆盘子(int cap) {
        this.maxCaps=cap;
        this.list=new ArrayList<>();
    }

    public void push(int val) {
        if (maxCaps<=0) return;
        if (list.isEmpty()){//第一个盘子堆
            Stack<Integer> arr = new Stack<>();
            arr.push(val);
            list.add(arr);
        }
        else {
            Stack<Integer> oldArr = list.get(list.size() - 1);
            int size=oldArr.size();
            if (size==maxCaps){//需要新建1个栈
                Stack<Integer> arr = new Stack<>();
                arr.push(val);
                list.add(arr);
            }else {
                oldArr.add(val);
            }
        }
    }

    public int pop() {
        if (list.size()==0) return -1;
        Stack<Integer> oldArr = list.get(list.size() - 1);
        int re = oldArr.peek();
        oldArr.pop();
        if (oldArr.size()==0) list.remove(list.size() - 1);
        return re;
    }

    public int popAt(int index) {
        if (list.size()==0||index<0||index>list.size()-1) return -1;
        Stack<Integer> oldArr = list.get(index);
        int re = oldArr.peek();
        oldArr.pop();
        if (oldArr.size()==0) list.remove(index);
        return re;
    }


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值