左神算法课笔记(二):链表、栈和队列、递归Master公式、哈希表、有序表
單向鏈表
雙向鏈表
單鏈表、雙鏈表最簡單的面試題
1、單鏈表和雙鏈表如何反轉
package class02;import java.util.ArrayList;public class Code01_ReverseList {public static class Node {public int value;public Node next;public Node(int data) {value = data;}}public static class DoubleNode {public int value;public DoubleNode last;public DoubleNode next;public DoubleNode(int data) {value = data;}}public static Node reverseLinkedList(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 DoubleNode reverseDoubleList(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;}public static Node testReverseLinkedList(Node head) {if (head == null) {return null;}ArrayList<Node> list = new ArrayList<>();while (head != null) {list.add(head);head = head.next;}list.get(0).next = null;int N = list.size();for (int i = 1; i < N; i++) {list.get(i).next = list.get(i - 1);}return list.get(N - 1);}public static DoubleNode testReverseDoubleList(DoubleNode head) {if (head == null) {return null;}ArrayList<DoubleNode> list = new ArrayList<>();while (head != null) {list.add(head);head = head.next;}list.get(0).next = null;DoubleNode pre = list.get(0);int N = list.size();for (int i = 1; i < N; i++) {DoubleNode cur = list.get(i);cur.last = null;cur.next = pre;pre.last = cur;pre = cur;}return list.get(N - 1);}public static Node generateRandomLinkedList(int len, int value) {int size = (int) (Math.random() * (len + 1));if (size == 0) {return null;}size--;Node head = new Node((int) (Math.random() * (value + 1)));Node pre = head;while (size != 0) {Node cur = new Node((int) (Math.random() * (value + 1)));pre.next = cur;pre = cur;size--;}return head;}public static DoubleNode generateRandomDoubleList(int len, int value) {int size = (int) (Math.random() * (len + 1));if (size == 0) {return null;}size--;DoubleNode head = new DoubleNode((int) (Math.random() * (value + 1)));DoubleNode pre = head;while (size != 0) {DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1)));pre.next = cur;cur.last = pre;pre = cur;size--;}return head;}// 要求無環,有環別用這個函數public static boolean checkLinkedListEqual(Node head1, Node head2) {while (head1 != null && head2 != null) {if (head1.value != head2.value) {return false;}head1 = head1.next;head2 = head2.next;}return head1 == null && head2 == null;}// 要求無環,有環別用這個函數public static boolean checkDoubleListEqual(DoubleNode head1, DoubleNode head2) {boolean null1 = head1 == null;boolean null2 = head2 == null;if (null1 && null2) {return true;}if (null1 ^ null2) {return false;}if (head1.last != null || head2.last != null) {return false;}DoubleNode end1 = null;DoubleNode end2 = null;while (head1 != null && head2 != null) {if (head1.value != head2.value) {return false;}end1 = head1;end2 = head2;head1 = head1.next;head2 = head2.next;}if (head1 != null || head2 != null) {return false;}while (end1 != null && end2 != null) {if (end1.value != end2.value) {return false;}end1 = end1.last;end2 = end2.last;}return end1 == null && end2 == null;}public static void main(String[] args) {int len = 50;int value = 100;int testTime = 100000;for (int i = 0; i < testTime; i++) {Node node1 = generateRandomLinkedList(len, value);Node reverse1 = reverseLinkedList(node1);Node back1 = testReverseLinkedList(reverse1);if (!checkLinkedListEqual(node1, back1)) {System.out.println("oops!");break;}DoubleNode node2 = generateRandomDoubleList(len, value);DoubleNode reverse2 = reverseDoubleList(node2);DoubleNode back2 = testReverseDoubleList(reverse2);if (!checkDoubleListEqual(node2, back2)) {System.out.println("oops!");break;}}System.out.println("finish!");} }2、把給定值都刪除
package class02;public class Code02_DeleteGivenValue {public static class Node {public int value;public Node next;public Node(int data) {this.value = data;}}public static Node removeValue(Node head, int num) {while (head != null) {if (head.value != num) {break;}head = head.next;}// head來到 第一個不需要刪的位置Node pre = head;Node cur = head;// while (cur != null) {if (cur.value == num) {pre.next = cur.next;} else {pre = cur;}cur = cur.next;}return head;} }棧和隊列
棧
隊列
棧和隊列的實際實現:
- 雙向鏈表實現(頭指針、尾指針)提供四種方法:從頭部進、從頭部出、從尾部進、從尾部出
- 數組實現
1、雙向鏈表實現
package class02;import java.util.LinkedList; import java.util.Queue; import java.util.Stack;public class Code03_DoubleEndsQueueToStackAndQueue {public static class Node<T> {public T value;public Node<T> last;public Node<T> next;public Node(T data) {value = data;}}public static class DoubleEndsQueue<T> {public Node<T> head;public Node<T> tail;public void addFromHead(T value) {Node<T> cur = new Node<T>(value);if (head == null) {head = cur;tail = cur;} else {cur.next = head;head.last = cur;head = cur;}}public void addFromBottom(T value) {Node<T> cur = new Node<T>(value);if (head == null) {head = cur;tail = cur;} else {cur.last = tail;tail.next = cur;tail = cur;}}public T popFromHead() {if (head == null) {return null;}Node<T> cur = head;if (head == tail) {head = null;tail = null;} else {head = head.next;cur.next = null;head.last = null;}return cur.value;}public T popFromBottom() {if (head == null) {return null;}Node<T> cur = tail;if (head == tail) {head = null;tail = null;} else {tail = tail.last;tail.next = null;cur.last = null;}return cur.value;}public boolean isEmpty() {return head == null;}}public static class MyStack<T> {private DoubleEndsQueue<T> queue;public MyStack() {queue = new DoubleEndsQueue<T>();}public void push(T value) {queue.addFromHead(value);}public T pop() {return queue.popFromHead();}public boolean isEmpty() {return queue.isEmpty();}}public static class MyQueue<T> {private DoubleEndsQueue<T> queue;public MyQueue() {queue = new DoubleEndsQueue<T>();}public void push(T value) {queue.addFromHead(value);}public T poll() {return queue.popFromBottom();}public boolean isEmpty() {return queue.isEmpty();}}public static boolean isEqual(Integer o1, Integer o2) {if (o1 == null && o2 != null) {return false;}if (o1 != null && o2 == null) {return false;}if (o1 == null && o2 == null) {return true;}return o1.equals(o2);}public static void main(String[] args) {int oneTestDataNum = 100;int value = 10000;int testTimes = 100000;for (int i = 0; i < testTimes; i++) {MyStack<Integer> myStack = new MyStack<>();MyQueue<Integer> myQueue = new MyQueue<>();Stack<Integer> stack = new Stack<>();Queue<Integer> queue = new LinkedList<>();for (int j = 0; j < oneTestDataNum; j++) {int nums = (int) (Math.random() * value);if (stack.isEmpty()) {myStack.push(nums);stack.push(nums);} else {if (Math.random() < 0.5) {myStack.push(nums);stack.push(nums);} else {if (!isEqual(myStack.pop(), stack.pop())) {System.out.println("oops!");}}}int numq = (int) (Math.random() * value);if (stack.isEmpty()) {myQueue.push(numq);queue.offer(numq);} else {if (Math.random() < 0.5) {myQueue.push(numq);queue.offer(numq);} else {if (!isEqual(myQueue.poll(), queue.poll())) {System.out.println("oops!");}}}}}System.out.println("finish!");} }2、數組實現
動態數組 / 固定大小的靜態數組
3、實現一個特殊額棧
維護一個最小棧。
普通棧正常使用,最小棧存放的是每一個狀態下當前數的最小值
普通棧和最小棧同步push、pop,只不過給用戶返回的是普通棧里的內容
4、如何用隊列實現一個棧
兩個隊列,都是從頭進、從尾出
data隊列
help隊列
例如,現在要push進1,2,3,4,5
現在要 pop 1,2,3,4,5
(除了最后一個數5以外,剩余的移動到help隊列中,留下5用來給用戶返回,更改data隊列和help隊列的屬性,這樣以此類推)
5、如何用棧實現一個隊列
維護兩個棧:
push棧,pop棧
現用戶給我1,2,3,4,5
現在我要pop
(1)pop棧為空的時候才能往外導數據
(2)如果決定導數據,push棧在導的過程中要一次性的導完
只要滿足上面兩個原則,不管什么時候導數據,都是對的
遞歸
例子
遞歸函數的思維導圖
下面這個解法的復雜度是O(n)
package class02;public class Code08_GetMax {// 求arr中的最大值public static int getMax(int[] arr) {return process(arr, 0, arr.length - 1);}// arr[L..R]范圍上求最大值 L ... R Npublic static int process(int[] arr, int L, int R) {if (L == R) { // arr[L..R]范圍上只有一個數,直接返回,base casereturn arr[L];}int mid = L + ((R - L) >> 1); // 中點 1int leftMax = process(arr, L, mid);int rightMax = process(arr, mid + 1, R);return Math.max(leftMax, rightMax);} }遞歸在語言上是怎么實現的?
遞歸實際上是運用的系統棧
任何遞歸都可以改成非遞歸。
“尾遞歸”是一些語言對遞歸行為進行的優化,在底層執行的過程中已經是迭代了。
對于某一類遞歸,它的時間復雜度是可以直接確定的:
子問題的規模是N/b,子問題被調用a次,除去遞歸調用過程之外剩下所有行為的時間復雜度是O(n^d)
則時間復雜度可以直接確定如下(Master公式):
比如說我們上面這個問題就是
哈希表 HashMap
有序表 TreeMap
有序表的特點在于,你可以亂序插入元素,但他自己內部是有序的。
但是它的時間復雜度是O(logn)
有序表的底層實現可以是AVL樹/SB樹/紅黑樹,或跳表
非基礎類型在有序表中,怎么比較大小?以后會在堆的章節講。
總結
以上是生活随笔為你收集整理的左神算法课笔记(二):链表、栈和队列、递归Master公式、哈希表、有序表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 坦克大战 - 设计模式、BIO、NIO、
- 下一篇: 带你理清 Java 混乱的日志体系 -