第三课:队列,栈,矩阵,链表题目

左程云算法初级班 第三课

  1. 用数组结构实现大小固定的队列和栈

在这里插入图片描述

  • 数组实现栈:设置一个指针指向栈顶元素(上图中的size);当压入或者弹出元素时,改变指针指向;指针指向数组最后一个元素时,代表栈已被压满。
  • 数组实现队列:设置两个指针、一个指向队列第一个元素(上图中的first),一个指向队列最后一个元素(上图中的last);同时设置一个size记录队列中元素个数;每当队列中push入元素时,last往后移动,并将新的元素存入该位置,此时size++,若已经移到数组最末端且队列中元素个数少于数组长度时,last移动到数组首位;每当队列中poll出元素时,将first位置元素输出,并将first后移,此时size–,若已经移到数组最末端并且队列中元素个数大于0且少于数组长度时,first移动到数组首位;
  • 代码:
 public static class ArrayStack {
     private Integer[] arr;
     private Integer size;

     public ArrayStack(int initSize) {
         if (initSize < 0) {
             throw new IllegalArgumentException("The init size is less than 0");
         }
         arr = new Integer[initSize];
         size = 0;
     }

     public Integer peek() {
         if (size == 0) {
             return null;
         }
         return arr[size - 1];
     }

     public void push(int obj) {
         if (size == arr.length) {
             throw new ArrayIndexOutOfBoundsException("The queue is full");
         }
         arr[size++] = obj;
     }

     public Integer pop() {
         if (size == 0) {
             throw new ArrayIndexOutOfBoundsException("The queue is empty");
         }
         return arr[--size];
     }
 }

 public static class ArrayQueue {
     private Integer[] arr;
     private Integer size;
     private Integer first;
     private Integer last;

     public ArrayQueue(int initSize) {
         if (initSize < 0) {
             throw new IllegalArgumentException("The init size is less than 0");
         }
         arr = new Integer[initSize];
         size = 0;
         first = 0;
         last = 0;
     }

     public Integer peek() {
         if (size == 0) {
             return null;
         }
         return arr[first];
     }

     public void push(int obj) {
         if (size == arr.length) {
             throw new ArrayIndexOutOfBoundsException("The queue is full");
         }
         size++;
         arr[last] = obj;
         last = last == arr.length - 1 ? 0 : last + 1;
     }

     public Integer poll() {
         if (size == 0) {
             throw new ArrayIndexOutOfBoundsException("The queue is empty");
         }
         size--;
         int tmp = first;
         first = first == arr.length - 1 ? 0 : first + 1;
         return arr[tmp];
     }
 }
  1. 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

【要求】
1.pop、push、getMin操作的时间复杂度都是O(1)。
2.设计的栈类型可以使用现成的栈结构。

  • 方案一:准备两个栈,一个为基本栈,实现pop,push,另外一个存放最小值;在基本栈push时,若被push的值小于或者等于最小值栈中的最小值时,则将该值push进最小值栈;当基本栈pop时,若被pop的值等于最小值栈的栈顶元素时,最小值栈pop;栈中最小元素为最小值栈的栈顶元素;
  • 方案二: 准备两个栈,一个为基本栈,另外一个存放最小值;当基本栈push时,若被push的元素大于或等于最小值栈的栈顶元素,则最小值栈将自己的栈顶元素再push到栈顶一次,若被push的元素小于最小值栈的栈顶元素,则将该元素push到最小值栈;当基本栈pop时最小值栈pop;栈中最小元素为最小值栈的栈顶元素。
  • 代码:
 public static class MyStack1 {
     private Stack<Integer> stackData;
     private Stack<Integer> stackMin;

     public MyStack1() {
         this.stackData = new Stack<Integer>();
         this.stackMin = new Stack<Integer>();
     }

     public void push(int newNum) {
         if (this.stackMin.isEmpty()) {
             this.stackMin.push(newNum);
         } else if (newNum <= this.getmin()) {
             this.stackMin.push(newNum);
         }
         this.stackData.push(newNum);
     }

     public int pop() {
         if (this.stackData.isEmpty()) {
             throw new RuntimeException("Your stack is empty.");
         }
         int value = this.stackData.pop();
         if (value == this.getmipushn()) {
             this.stackMin.pop();
         }
         return value;
     }

     public int getmin() {
         if (this.stackMin.isEmpty()) {
             throw new RuntimeException("Your stack is empty.");
         }
         return this.stackMin.peek();
     }
 }

 public static class MyStack2 {
     private Stack<Integer> stackData;
     private Stack<Integer> stackMin;

     public MyStack2() {
         this.stackData = new Stack<Integer>();
         this.stackMin = new Stack<Integer>();
     }

     public void push(int newNum) {
         if (this.stackMin.isEmpty()) {
             this.stackMin.push(newNum);
         } else if (newNum < this.getmin()) {
             this.stackMin.push(newNum);
         } else {
             int newMin = this.stackMin.peek();
             this.stackMin.push(newMin);
         }
         this.stackData.push(newNum);
     }

     public int pop() {
         if (this.stackData.isEmpty()) {
             throw new RuntimeException("Your stack is empty.");
         }
         this.stackMin.pop();
         return this.stackData.pop();
     }

     public int getmin() {
         if (this.stackMin.isEmpty()) {
             throw new RuntimeException("Your stack is empty.");
         }
         return this.stackMin.peek();
     }
 }
  1. 如何仅用队列结构实现栈结构?如何仅用栈结构实现队列结构?
    • 队列实现栈:使用两个队列实现栈结构,一个队列为数据队列queue,一个数列为辅助队列help;当栈push时,queue进行add;当栈pop时,将queue中的元素依次poll并add入help,直到queue中仅剩一个元素,这是将这个元素poll出来,并将help与queue交换;当栈peek时,类似于push时的操作,只不过将queue中仅剩的一个元素poll出来后,要将其add入help;
    • 代码:
    public static class TwoQueuesStack {
        private Queue<Integer> queue;
        private Queue<Integer> help;
    
        public TwoQueuesStack() {
            queue = new LinkedList<Integer>();
            help = new LinkedList<Integer>();
        }
    
        public void push(int pushInt) {
            queue.add(pushInt);
        }
    
        public int peek() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            help.add(res);
            swap();
            return res;
        }
    
        public int pop() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() > 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            swap();
            return res;
        }
    
        private void swap() {
            Queue<Integer> tmp = help;
            help = queue;
            queue = tmp;
        }
    }
    
    • 栈实现队列:使用两个栈实现队列,一个栈stackPush进行push操作,一个栈stackPop进行pop操作;当队列push时,将元素push到stackPush;当队列poll时,若stackPop为空,则将stackPush中的所有元素全部倒入stackPop中,然后stackPop进行pop,若stackPop不为空,则直接stackPop进行pop;当队列peek时,若stackPop为空,则将stackPush中的所有元素全部倒入stackPop中,然后stackPop进行peek,若stackPop不为空,则直接stackPop进行peek。
    • 代码:
    public static class TwoStacksQueue {
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;
    
        public TwoStacksQueue() {
            stackPush = new Stack<Integer>();
            stackPop = new Stack<Integer>();
        }
    
        public void push(int pushInt) {
            stackPush.push(pushInt);
        }
    
        public int poll() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.pop();
        }
    
        public int peek() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.peek();
        }
    }
    
  2. 猫狗队列 【题目】 宠物、狗和猫的类如下:

public class Pet {
private String type;
public Pet(String type) { this.type = type; }
public String getPetType() { return this.type; }
}
public class Dog extends Pet {
public Dog() { super(“dog”); }
}
public class Cat extends Pet {
public Cat() { super(“cat”); }
}
实现一种狗猫队列的结构,要求如下: 用户可以调用add方法将cat类或dog类的
实例放入队列中; 用户可以调用pollAll方法,将队列中所有的实例按照进队列
的先后顺序依次弹出; 用户可以调用pollDog方法,将队列中dog类的实例按照
进队列的先后顺序依次弹出; 用户可以调用pollCat方法,将队列中cat类的实
例按照进队列的先后顺序依次弹出; 用户可以调用isEmpty方法,检查队列中是
否还有dog或cat的实例; 用户可以调用isDogEmpty方法,检查队列中是否有dog
类的实例; 用户可以调用isCatEmpty方法,检查队列中是否有cat类的实例。

  • 定义一个类(PetEnterQueue),属性为pet和count。其中count为时间戳,用来区分Dog与Cat进入队列的顺序;
  • 当猫狗队列(DogCatQueue)中进入Dog时,将带有时间戳的PetEnterQueue(其中装的pet为Dog)放入DogCatQueue中的dogQ;当猫狗队列(DogCatQueue)中进入Cat时,将带有时间戳的PetEnterQueue(其中装的pet为Cat)放入DogCatQueue中的catQ;在pollAll操作时,根据队列中PetEnterQueue的时间戳count依次从dogQ和catQ中poll;在pollDog操作时,从dogQ中poll;在pollCat操作时,从catQ中poll;isEmpty方法、isDogEmpty方法、isCatEmpty方法用相同原理实现。
  • 代码:
  public class Code_04_DogCatQueue {
 
     public static class Pet {
         private String type;
         public Pet(String type) {
             this.type = type;
         }
         public String getPetType() {
             return this.type;
         }
     }
     public static class Dog extends Pet {
         public Dog() {
             super("dog");
         }
     }
     public static class Cat extends Pet {
         public Cat() {
             super("cat");
         }
     }
     public static class PetEnterQueue {
         private Pet pet;
         private long count;
         public PetEnterQueue(Pet pet, long count) {
             this.pet = pet;
             this.count = count;
         }
         public Pet getPet() {
             return this.pet;
         }
         public long getCount() {
             return this.count;
         }
         public String getEnterPetType() {
             return this.pet.getPetType();
         }
     }
     public static class DogCatQueue {
         private Queue<PetEnterQueue> dogQ;
         private Queue<PetEnterQueue> catQ;
         private long count;
 
         public DogCatQueue() {
             this.dogQ = new LinkedList<PetEnterQueue>();
             this.catQ = new LinkedList<PetEnterQueue>();
             this.count = 0;
         }
 
         public void add(Pet pet) {
             if (pet.getPetType().equals("dog")) {
                 this.dogQ.add(new PetEnterQueue(pet, this.count++));
             } else if (pet.getPetType().equals("cat")) {
                 this.catQ.add(new PetEnterQueue(pet, this.count++));
             } else {
                 throw new RuntimeException("err, not dog or cat");
             }
         }
 
         public Pet pollAll() {
             if (!this.dogQ.isEmpty() && !this.catQ.isEmpty()) {
                 if (this.dogQ.peek().getCount() < this.catQ.peek().getCount()) {
                     return this.dogQ.poll().getPet();
                 } else {
                     return this.catQ.poll().getPet();
                 }
             } else if (!this.dogQ.isEmpty()) {
                 return this.dogQ.poll().getPet();
             } else if (!this.catQ.isEmpty()) {
                 return this.catQ.poll().getPet();
             } else {
                 throw new RuntimeException("err, queue is empty!");
             }
         }
 
         public Dog pollDog() {
             if (!this.isDogQueueEmpty()) {
                 return (Dog) this.dogQ.poll().getPet();
             } else {
                 throw new RuntimeException("Dog queue is empty!");
             }
         }
 
         public Cat pollCat() {
             if (!this.isCatQueueEmpty()) {
                 return (Cat) this.catQ.poll().getPet();
             } else
                 throw new RuntimeException("Cat queue is empty!");
         }
 
         public boolean isEmpty() {
             return this.dogQ.isEmpty() && this.catQ.isEmpty();
         }
 
         public boolean isDogQueueEmpty() {
             return this.dogQ.isEmpty();
         }
 
         public boolean isCatQueueEmpty() {
             return this.catQ.isEmpty();
         }
      } 
 }
  1. 转圈打印矩阵

【题目】 给定一个整型矩阵matrix,请按照转圈的方式打印它。
例如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
15 16 打印结果为:1,2,3,4,8,12,16,15,14,13,9,
5,6,7,11, 10
【要求】 额外空间复杂度为O(1)。
在这里插入图片描述

  • 实现上图所描述的方法打印矩阵边界,然后迭代调用;
  • 代码:
public static void spiralOrderPrint(int[][] matrix) {
     int tR = 0;
     int tC = 0;
     int dR = matrix.length - 1;
     int dC = matrix[0].length - 1;
     while (tR <= dR && tC <= dC) {
         printEdge(matrix, tR++, tC++, dR--, dC--);
     }
 }

 public static void printEdge(int[][] m, int tR, int tC, int dR, int dC) {
     if (tR == dR) {
         for (int i = tC; i <= dC; i++) {
             System.out.print(m[tR][i] + " ");
         }
     } else if (tC == dC) {
         for (int i = tR; i <= dR; i++) {
             System.out.print(m[i][tC] + " ");
         }
     } else {
         int curC = tC;
         int curR = tR;
         while (curC != dC) {
             System.out.print(m[tR][curC] + " ");
             curC++;
         }
         while (curR != dR) {
             System.out.print(m[curR][dC] + " ");
             curR++;
         }
         while (curC != tC) {
             System.out.print(m[dR][curC] + " ");
             curC--;
         }
         while (curR != tR) {
             System.out.print(m[curR][tC] + " ");
             curR--;
         }
     }
 }
  1. 旋转正方形矩阵

【题目】 给定一个整型正方形矩阵matrix,请把该矩阵调整成顺时针旋转90度的样子。
【要求】 额外空间复杂度为O(1)。
在这里插入图片描述

  • 实现上图所描述的方法旋转矩阵边界,然后迭代调用;
  • 代码:
 public static void rotate(int[][] matrix) {
     int tR = 0;
     int tC = 0;
     int dR = matrix.length - 1;
     int dC = matrix[0].length - 1;
     while (tR < dR) {
         rotateEdge(matrix, tR++, tC++, dR--, dC--);
     }
 }

 public static void rotateEdge(int[][] m, int tR, int tC, int dR, int dC) {
     int times = dC - tC;
     int tmp = 0;
     for (int i = 0; i != times; i++) {
         tmp = m[tR][tC + i];
         m[tR][tC + i] = m[dR - i][tC];
         m[dR - i][tC] = m[dR][dC - i];
         m[dR][dC - i] = m[tR + i][dC];
         m[tR + i][dC] = tmp;
     }
 }
 ```
7. 反转单向和双向链表
> 
【题目】 分别实现反转单向链表和反转双向链表的函数。
【要求】 如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)

 * 代码:
 ```java
public static class Node {
     public int value;
     public Node next;

     public Node(int data) {
         this.value = data;
     }
 }

 public static Node reverseList(Node head) {
     Node pre = null;
     Node next = null;
     while (head != null) {
         next = head.next;
         head.next = pre;
         pre = head;
         head = next;
     }
     return pre;
 }

 public static class DoubleNode {
     public int value;
     public DoubleNode last;
     public DoubleNode next;

     public DoubleNode(int data) {
         this.value = data;
     }
 }

 public static DoubleNode reverseList(DoubleNode head) {
     DoubleNode pre = null;
     DoubleNode next = null;
     while (head != null) {
         next = head.next;
         head.next = pre;
         head.last = next;
         pre = head;
         head = next;
     }
     return pre;
 }
  1. “之”字形打印矩阵

【题目】 给定一个矩阵matrix,按照“之”字形的方式打印这个矩阵,例如: 1 2 3 4 ,5 6 7 8, 9 10 11 12“之”字形打印的结果为:1,2,5,9,6,3,4,7,10,11,8,12
【要求】 额外空间复杂度为O(1)。
在这里插入图片描述

  • 如上图,定位每条射线两端,通过标志位确定选择射线方向,实现“之”字形打印矩阵;
  • 代码:
public static void printMatrixZigZag(int[][] matrix) {
     int tR = 0;
     int tC = 0;
     int dR = 0;
     int dC = 0;
     int endR = matrix.length - 1;
     int endC = matrix[0].length - 1;
     boolean fromUp = false;
     while (tR != endR + 1) {
         printLevel(matrix, tR, tC, dR, dC, fromUp);
         tR = tC == endC ? tR + 1 : tR;
         tC = tC == endC ? tC : tC + 1;
         dC = dR == endR ? dC + 1 : dC;
         dR = dR == endR ? dR : dR + 1;
         fromUp = !fromUp;
     }
     System.out.println();
 }

 public static void printLevel(int[][] m, int tR, int tC, int dR, int dC,
         boolean f) {
     if (f) {
         while (tR != dR + 1) {
             System.out.print(m[tR++][tC--] + " ");
         }
     } else {
         while (dR != tR - 1) {
             System.out.print(m[dR--][dC++] + " ");
         }
     }
 }
  1. 在行列都排好序的矩阵中找数

【题目】 给定一个有N*M的整型矩阵matrix和一个整数K,matrix的每一行和每一 列都是排好序的。实现一个函数,判断K是否在matrix中。 例如: 0 1 2 5 ,2 3 4 7 ,4 4 4 8, 5 7 7 9 如果K为7,返回true;如果K为6,返回false。
【要求】 时间复杂度为O(N+M),额外空间复杂度为O(1)。
在这里插入图片描述

  • 代码:
 public static boolean isContains(int[][] matrix, int K) {
     int row = 0;
     int col = matrix[0].length - 1;
     while (row < matrix.length && col > -1) {
         if (matrix[row][col] == K) {
             return true;
         } else if (matrix[row][col] > K) {
             col--;
         } else {
             row++;
         }
     }
     return false;
 }
  1. 打印两个有序链表的公共部分

【题目】 给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。

  • 比较两个有序链表的当前值,若不等,较小的链表取下一节点;若相等,则打印当前值,两个链表都取下一节点;
  • 代码:
public static class Node {
     public int value;
     public Node next;
     public Node(int data) {
         this.value = data;
     }
 }

 public static void printCommonPart(Node head1, Node head2) {
     System.out.print("Common Part: ");
     while (head1 != null && head2 != null) {
         if (head1.value < head2.value) {
             head1 = head1.next;
         } else if (head1.value > head2.value) {
             head2 = head2.next;
         } else {
             System.out.print(head1.value + " ");
             head1 = head1.next;
             head2 = head2.next;
         }
     }
     System.out.println();
 }
  1. 判断一个链表是否为回文结构

【题目】 给定一个链表的头节点head,请判断该链表是否为回文结构。 例如: 1->2->1,返回true。 1->2->2->1,返回true。15->6->15,返回true。 1->2->3,返回false。
进阶: 如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。

  • 方案一:申请一个栈,遍历链表并将各结点的值压入栈,再次遍历链表,与栈中pop出的值比较;
  • 方案二:设置一个快指针和一个慢指针,同时遍历链表,当快指针到链表末尾时,慢指针到链表中间,申请一个栈,通过慢指针将链表后半部分压入栈中;然后比较链表前半部分与栈中的值;
  • 方案三(进阶):设置一个快指针和一个慢指针,同时遍历链表,当快指针到链表末尾时,慢指针到链表中间,将链表的后半部分的方向反过来,链表中间值的next指向null,然后分别从链表头与链表为开始比较,若出现不相等情况,返回false;若未出现,则将链表后半部分恢复原始状态并返回true。
  • 代码:
// need n extra space
 public static boolean isPalindrome1(Node head) {
     Stack<Node> stack = new Stack<Node>();
     Node cur = head;
     while (cur != null) {
         stack.push(cur);
         cur = cur.next;
     }
     while (head != null) {
         if (head.value != stack.pop().value) {
             return false;
         }
         head = head.next;
     }
     return true;
 }

 // need n/2 extra space
 public static boolean isPalindrome2(Node head) {
     if (head == null || head.next == null) {
         return true;
     }
     Node right = head.next;
     Node cur = head;
     while (cur.next != null && cur.next.next != null) {
         right = right.next;
         cur = cur.next.next;
     }
     Stack<Node> stack = new Stack<Node>();
     while (right != null) {
         stack.push(right);
         right = right.next;
     }
     while (!stack.isEmpty()) {
         if (head.value != stack.pop().value) {
             return false;
         }
         head = head.next;
     }
     return true;
 }

 // need O(1) extra space
 public static boolean isPalindrome3(Node head) {
     if (head == null || head.next == null) {
         return true;
     }
     Node n1 = head;
     Node n2 = head;
     while (n2.next != null && n2.next.next != null) { // find mid node
         n1 = n1.next; // n1 -> mid
         n2 = n2.next.next; // n2 -> end
     }
     n2 = n1.next; // n2 -> right part first node
     n1.next = null; // mid.next -> null
     Node n3 = null;
     while (n2 != null) { // right part convert
         n3 = n2.next; // n3 -> save next node
         n2.next = n1; // next of right node convert
         n1 = n2; // n1 move
         n2 = n3; // n2 move
     }
     n3 = n1; // n3 -> save last node
     n2 = head;// n2 -> left first node
     boolean res = true;
     while (n1 != null && n2 != null) { // check palindrome
         if (n1.value != n2.value) {
             res = false;
             break;
         }
         n1 = n1.next; // left to mid
         n2 = n2.next; // right to mid
     }
     n1 = n3.next;
     n3.next = null;
     while (n1 != null) { // recover list
         n2 = n1.next;
         n1.next = n3;
         n3 = n1;
         n1 = n2;
     }
     return res;
 }
  1. 将单向链表按某值划分成左边小、中间相等、右边大的形式

【题目】 给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整 数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于 pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于 pivot的节点。除这个要求外,对调整后的节点顺序没有更多的要求。 例如:链表9->0->4->5->1,pivot=3。 调整后链表可以是1->0->4->9->5,也可以是0->1->9->5->4。总之,满 足左部分都是小于3的节点,中间部分都是等于3的节点(本例中这个部分为空),右部分都是大于3的节点即可。对某部分内部的节点顺序不做 要求。
进阶: 在原问题的要求之上再增加如下两个要求。在左、中、右三个部分的内部也做顺序要求,要求每部分里的节点从左 到右的顺序与原链表中节点的先后次序一致。 例如:链表9->0->4->5->1,pivot=3。调整后的链表是0->1->9->4->5。 在满足原问题要求的同时,左部分节点从左到
右为0、1。在原链表中也 是先出现0,后出现1;中间部分在本例中为空,不再讨论;右部分节点 从左到右为9、4、5。在原链表中也是先出现9,然后出现4,最后出现5。如果链表长度为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)。

  • 方案一:遍历链表,记录链表长度,然后申请一个同长度的数组,将链表各结点装入数组,然后采用荷兰国旗的方法;
  • 代码:
 public static Node listPartition1(Node head, int pivot) {
     if (head == null) {
         return head;
     }
     Node cur = head;
     int i = 0;
     while (cur != null) {
         i++;
         cur = cur.next;
     }
     Node[] nodeArr = new Node[i];
     i = 0;
     cur = head;
     for (i = 0; i != nodeArr.length; i++) {
         nodeArr[i] = cur;
         cur = cur.next;
     }
     arrPartition(nodeArr, pivot);
     for (i = 1; i != nodeArr.length; i++) {
         nodeArr[i - 1].next = nodeArr[i];
     }
     nodeArr[i - 1].next = null;
     return nodeArr[0];
 }

 public static void arrPartition(Node[] nodeArr, int pivot) {
     int small = -1;
     int big = nodeArr.length;
     int index = 0;
     while (index != big) {
         if (nodeArr[index].value < pivot) {
             swap(nodeArr, ++small, index++);
         } else if (nodeArr[index].value == pivot) {
             index++;
         } else {
             swap(nodeArr, --big, index);
         }
     }
 }
 public static void swap(Node[] nodeArr, int a, int b) {
     Node tmp = nodeArr[a];
     nodeArr[a] = nodeArr[b];
     nodeArr[b] = tmp;
 }
  • 方案二(进阶):遍历链表,分别将>、=、<部分的节点按照顺序做成三条新的小链表,遍历结束后,再将三条小链表接起来;
  • 代码:
public static Node listPartition2(Node head, int pivot) {
     Node sH = null; // small head
     Node sT = null; // small tail
     Node eH = null; // equal head
     Node eT = null; // equal tail
     Node bH = null; // big head
     Node bT = null; // big tail
     Node next = null; // save next node
     // every node distributed to three lists
     while (head != null) {
         next = head.next;
         head.next = null;
         if (head.value < pivot) {
             if (sH == null) {
                 sH = head;
                 sT = head;
             } else {
                 sT.next = head;
                 sT = head;
             }
         } else if (head.value == pivot) {
             if (eH == null) {
                 eH = head;
                 eT = head;
             } else {
                 eT.next = head;
                 eT = head;
             }
         } else {
             if (bH == null) {
                 bH = head;
                 bT = head;
             } else {
                 bT.next = head;
                 bT = head;
             }
         }
         head = next;
     }
     // small and equal reconnect
     if (sT != null) {
         sT.next = eH;
         eT = eT == null ? sT : eT;
     }
     // all reconnect
     if (eT != null) {
         eT.next = bH;
     }
     return sH != null ? sH : eH != null ? eH : bH;
 }
  1. 复制含有随机指针节点的链表

【题目】 一种特殊的链表节点类描述如下:
public class Node { public int value; public Node next; public
Node rand;
public Node(int data) { this.value = data; }
}
Node类中的value是节点值,next指针和正常单链表中next指针的意义一 样,都指向下一个节点,rand指针是Node类中新增的指针,这个指针可 能指向链表中的任意一个节点,也可能指向null。 给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个 函数完成这个链表中所有结构的复制,并返回复制的新链表的头节点。
进阶:不使用额外的数据结构,只用有限几个变量,且在时间复杂度为 O(N)内完成原问题要实现的函数。

  • 方案一:使用HashMap,将链表遍历并以结点为key,用该结点的value创建一个新的Node结点为value放入HashMap;然后依据原链表将HashMap中的各结点的相互关系复制;
  • 方案二(进阶):将链表中各结点的复制结点放在原结点后,然后复制出复制结点的rand指向关系,最后将原链表结点与复制链表的节点分开;
  • 代码:
public static Node copyListWithRand1(Node head) {
     HashMap<Node, Node> map = new HashMap<Node, Node>();
     Node cur = head;
     while (cur != null) {
         map.put(cur, new Node(cur.value));
         cur = cur.next;
     }
     cur = head;
     while (cur != null) {
         map.get(cur).next = map.get(cur.next);
         map.get(cur).rand = map.get(cur.rand);
         cur = cur.next;
     }
     return map.get(head);
 }

 public static Node copyListWithRand2(Node head) {
     if (head == null) {
         return null;
     }
     Node cur = head;
     Node next = null;
     // copy node and link to every node
     while (cur != null) {
         next = cur.next;
         cur.next = new Node(cur.value);
         cur.next.next = next;
         cur = next;
     }
     cur = head;
     Node curCopy = null;
     // set copy node rand
     while (cur != null) {
         next = cur.next.next;
         curCopy = cur.next;
         curCopy.rand = cur.rand != null ? cur.rand.next : null;
         cur = next;
     }
     Node res = head.next;
     cur = head;
     // split
     while (cur != null) {
         next = cur.next.next;
         curCopy = cur.next;
         cur.next = next;
         curCopy.next = next != null ? next.next : null;
         cur = next;
     }
     return res;
 }
  1. 两个单链表相交的一系列问题

【题目】 在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数, 如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。 要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外空间复杂度请达到O(1)。
在这里插入图片描述

  • 根据Loop1与Loop2来进行分类判断;
  • 代码:
public static Node getIntersectNode(Node head1, Node head2) {
     if (head1 == null || head2 == null) {
         return null;
     }
     Node loop1 = getLoopNode(head1);
     Node loop2 = getLoopNode(head2);
     if (loop1 == null && loop2 == null) {
         return noLoop(head1, head2);
     }
     if (loop1 != null && loop2 != null) {
         return bothLoop(head1, loop1, head2, loop2);
     }
     return null;
 }

 public static Node getLoopNode(Node head) {
     if (head == null || head.next == null || head.next.next == null) {
         return null;
     }
     Node n1 = head.next; // n1 -> slow
     Node n2 = head.next.next; // n2 -> fast
     while (n1 != n2) {
         if (n2.next == null || n2.next.next == null) {
             return null;
         }
         n2 = n2.next.next;
         n1 = n1.next;
     }
     n2 = head; // n2 -> walk again from head
     while (n1 != n2) {
         n1 = n1.next;
         n2 = n2.next;
     }
     return n1;
 }

 public static Node noLoop(Node head1, Node head2) {
     if (head1 == null || head2 == null) {
         return null;
     }
     Node cur1 = head1;
     Node cur2 = head2;
     int n = 0;
     while (cur1.next != null) {
         n++;
         cur1 = cur1.next;
     }
     while (cur2.next != null) {
         n--;
         cur2 = cur2.next;
     }
     if (cur1 != cur2) {
         return null;
     }
     cur1 = n > 0 ? head1 : head2;
     cur2 = cur1 == head1 ? head2 : head1;
     n = Math.abs(n);
     while (n != 0) {
         n--;
         cur1 = cur1.next;
     }
     while (cur1 != cur2) {
         cur1 = cur1.next;
         cur2 = cur2.next;
     }
     return cur1;
 }

 public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
     Node cur1 = null;
     Node cur2 = null;
     if (loop1 == loop2) {
         cur1 = head1;
         cur2 = head2;
         int n = 0;
         while (cur1 != loop1) {
             n++;
             cur1 = cur1.next;
         }
         while (cur2 != loop2) {
             n--;
             cur2 = cur2.next;
         }
         cur1 = n > 0 ? head1 : head2;
         cur2 = cur1 == head1 ? head2 : head1;
         n = Math.abs(n);
         while (n != 0) {
             n--;
             cur1 = cur1.next;
         }
         while (cur1 != cur2) {
             cur1 = cur1.next;
             cur2 = cur2.next;
         }
         return cur1;
     } else {
         cur1 = loop1.next;
         while (cur1 != loop1) {
             if (cur1 == loop2) {
                 return loop1;
             }
             cur1 = cur1.next;
         }
         return null;
     }
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Timothy Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值