1 链表、栈、队列、递归、哈希

1.1 链表

1.1.1 单向链表

public class Node {
public int value;
public Node next;

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

}

1.1.2 双向链表

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

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

1.1.3 单双链表简单练习

1. 单链表和双链表如何反转

1 -> 2 -> 3 转换为 3 -> 2 -> 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;
}
}

// 翻转单向链表，传入头结点
Node pre = null;
Node next = null;
}
return pre;
}

// 翻转双向链表，传入头结点
public static DoubleNode reverseDoubleList(DoubleNode head) {
DoubleNode pre = null;
DoubleNode next = null;
}
return pre;
}

return null;
}
ArrayList<Node> list = new ArrayList<>();
}
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) {
return null;
}
ArrayList<DoubleNode> list = new ArrayList<>();
}
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)));
while (size != 0) {
Node cur = new Node((int) (Math.random() * (value + 1)));
pre.next = cur;
pre = cur;
size--;
}
}

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)));
while (size != 0) {
DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1)));
pre.next = cur;
cur.last = pre;
pre = cur;
size--;
}
}

// 要求无环，有环别用这个函数
return false;
}
}
}

// 要求无环，有环别用这个函数
boolean null1 = head1 == null;
boolean null2 = head2 == null;
if (null1 && null2) {
return true;
}
if (null1 ^ null2) {
return false;
}
return false;
}
DoubleNode end1 = null;
DoubleNode end2 = null;
return false;
}
}
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++) {
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!");

}

}

1. 把给定的值都删除

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) {
break;
}
}
//
while (cur != null) {
if (cur.value == num) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
}

}

Tips: Java中也有可能产生内存泄漏，与CPP不同，CPP的内存泄漏有可能是我们开辟了内存空间忘记释放。而Java的内存泄漏大可能是程序中的变量的生存周期引起的，如果该程序是一个类似定时任务的7*24小时不间断运行，那么申请的变量（数据结构）就有可能不会被及时释放。如果不注意往里面添加一些不必要的变量，这些变量就是内存泄漏

1.2 栈、队列

1. 逻辑概念

1. 底层实现方式

package class02;

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> tail;

// 从头部加节点
Node<T> cur = new Node<T>(value);
tail = cur;
} else {
}
}

// 从尾部加节点
Node<T> cur = new Node<T>(value);
tail = cur;
} else {
cur.last = tail;
tail.next = cur;
tail = cur;
}
}

// 从头部弹出节点
return null;
}
tail = null;
} else {
cur.next = null;
}
return cur.value;
}

// 从尾部弹出节点
public T popFromBottom() {
return null;
}
Node<T> cur = tail;
tail = null;
} else {
tail = tail.last;
tail.next = null;
cur.last = null;
}
return cur.value;
}

// 该双向链表结构是否为空
public boolean isEmpty() {
}

}

// 用上述双向链表结构实现栈
public static class MyStack<T> {
private DoubleEndsQueue<T> queue;

public MyStack() {
queue = new DoubleEndsQueue<T>();
}

public void push(T value) {
}

public T pop() {
}

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) {
}

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<>();
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!");
}

}

package class02;

public class Code04_RingArray {

public static class MyQueue {
// 数组结构
private int[] arr;
// 往当前队列添加数的下标位置
private int pushi;
// 当前队列需要出队列的位置
private int polli;
// 当前队列使用的空间大小
private int size;
// 数组最大大小，用户传入
private final int limit;

public MyQueue(int limit) {
arr = new int[limit];
pushi = 0;
polli = 0;
size = 0;
this.limit = limit;
}

public void push(int value) {
if (size == limit) {
throw new RuntimeException("栈满了，不能再加了");
}
size++;
arr[pushi] = value;
pushi = nextIndex(pushi);
}

public int pop() {
if (size == 0) {
throw new RuntimeException("栈空了，不能再拿了");
}
size--;
int ans = arr[polli];
polli = nextIndex(polli);
return ans;
}

public boolean isEmpty() {
return size == 0;
}

// 如果现在的下标是i，返回下一个位置，该实现可以实现环形的ringbuffer
private int nextIndex(int i) {
return i < limit - 1 ? i + 1 : 0;
}

}

}

1.3 栈、队列常见面试题

1、pop、push、getMin操作的时间复杂度都是O(1)

2、设计的栈类型可以使用现成的栈结构

package class02;

import java.util.Stack;

public class Code05_GetMinStack {

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.getmin()) {
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.");
}
// 弹出操作，同步弹出，保证大小一致，只返回给用户data栈中的内容即可
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();
}
}

public static void main(String[] args) {
MyStack1 stack1 = new MyStack1();
stack1.push(3);
System.out.println(stack1.getmin());
stack1.push(4);
System.out.println(stack1.getmin());
stack1.push(1);
System.out.println(stack1.getmin());
System.out.println(stack1.pop());
System.out.println(stack1.getmin());

System.out.println("=============");

MyStack1 stack2 = new MyStack1();
stack2.push(3);
System.out.println(stack2.getmin());
stack2.push(4);
System.out.println(stack2.getmin());
stack2.push(1);
System.out.println(stack2.getmin());
System.out.println(stack2.pop());
System.out.println(stack2.getmin());
}

}

/**
* 两个栈实现队列
**/
package class02;

import java.util.Stack;

public class Code06_TwoStacksImplementQueue {

public static class TwoStacksQueue {
public Stack<Integer> stackPush;
public Stack<Integer> stackPop;

public TwoStacksQueue() {
stackPush = new Stack<Integer>();
stackPop = new Stack<Integer>();
}

// push栈向pop栈倒入数据
private void pushToPop() {
if (stackPop.empty()) {
while (!stackPush.empty()) {
stackPop.push(stackPush.pop());
}
}
}

stackPush.push(pushInt);
pushToPop();
}

public int poll() {
if (stackPop.empty() && stackPush.empty()) {
throw new RuntimeException("Queue is empty!");
}
pushToPop();
return stackPop.pop();
}

public int peek() {
if (stackPop.empty() && stackPush.empty()) {
throw new RuntimeException("Queue is empty!");
}
pushToPop();
return stackPop.peek();
}
}

public static void main(String[] args) {
TwoStacksQueue test = new TwoStacksQueue();
System.out.println(test.peek());
System.out.println(test.poll());
System.out.println(test.peek());
System.out.println(test.poll());
System.out.println(test.peek());
System.out.println(test.poll());
}

}

/**
* 两个队列实现栈
**/
package class02;

import java.util.Queue;
import java.util.Stack;

public class Code07_TwoQueueImplementStack {

public static class TwoQueueStack<T> {
public Queue<T> queue;
public Queue<T> help;

public TwoQueueStack() {
}

public void push(T value) {
queue.offer(value);
}

public T poll() {
while (queue.size() > 1) {
help.offer(queue.poll());
}
T ans = queue.poll();
Queue<T> tmp = queue;
queue = help;
help = tmp;
return ans;
}

public T peek() {
while (queue.size() > 1) {
help.offer(queue.poll());
}
T ans = queue.poll();
help.offer(ans);
Queue<T> tmp = queue;
queue = help;
help = tmp;
return ans;
}

public boolean isEmpty() {
return queue.isEmpty();
}

}

public static void main(String[] args) {
System.out.println("test begin");
TwoQueueStack<Integer> myStack = new TwoQueueStack<>();
Stack<Integer> test = new Stack<>();
int testTime = 1000000;
int max = 1000000;
for (int i = 0; i < testTime; i++) {
if (myStack.isEmpty()) {
if (!test.isEmpty()) {
System.out.println("Oops");
}
int num = (int) (Math.random() * max);
myStack.push(num);
test.push(num);
} else {
if (Math.random() < 0.25) {
int num = (int) (Math.random() * max);
myStack.push(num);
test.push(num);
} else if (Math.random() < 0.5) {
if (!myStack.peek().equals(test.peek())) {
System.out.println("Oops");
}
} else if (Math.random() < 0.75) {
if (!myStack.poll().equals(test.pop())) {
System.out.println("Oops");
}
} else {
if (myStack.isEmpty() != test.isEmpty()) {
System.out.println("Oops");
}
}
}
}

System.out.println("test finish!");

}

}

1.4 递归

1、从思想上理解递归

2、从实现角度出发理解递归

1、 将[L...R]范围分成左右两半。左[L...Mid],右[Mid+1...R]
2、 左部分求最大值，右部分求最大值
3、[L...R]范围上的最大值，就是max{左部分最大值,右部分最大值}

2步骤是个递归过程，当范围上只有一个数，就可以不用再递归了

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   N
public static int process(int[] arr, int L, int R) {
if (L == R) { // arr[L..R]范围上只有一个数，直接返回，base case
return arr[L];
}
int mid = L + ((R - L) >> 1); // 中点
// 左部分最大值
int leftMax = process(arr, L, mid);
// 右部分最大值
int rightMax = process(arr, mid + 1, R);
return Math.max(leftMax, rightMax);
}

}

1.4.1 递归行为的时间复杂度

T(N) = aT(N/b) + O(N^d)

T(N) = 2T(N/2) + O(N^0)

logb^a > d   =>  O(N ^ (logb^a))

logb^a < d   =>  O(N^d)

logb^a == d   =>  O(N^d * logN)

1.5 哈希表HashMap、HashSet

Hash表的增删改查，在使用的时候，一律认为时间复杂度是O(1)的

1.6 顺序表 TreeMap、TreeSet

package class02;

import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeMap;

public class HashMapAndSortedMap {

public static class Node{
public int value;
public Node(int v) {
value = v;
}
}

public static void main(String[] args) {
// UnSortedMap
HashMap<Integer, String> map = new HashMap<>();
map.put(1000000, "我是1000000");
map.put(2, "我是2");
map.put(3, "我是3");
map.put(4, "我是4");
map.put(5, "我是5");
map.put(6, "我是6");
map.put(1000000, "我是1000001");

System.out.println(map.containsKey(1));
System.out.println(map.containsKey(10));

System.out.println(map.get(4));
System.out.println(map.get(10));

map.put(4, "他是4");
System.out.println(map.get(4));

map.remove(4);
System.out.println(map.get(4));

//       key
HashSet<String>  set = new HashSet<>();
set.contains("abc");
set.remove("abc");

// 哈希表，增、删、改、查，在使用时，O（1）

System.out.println("=====================");

int a = 100000;
int b = 100000;
System.out.println(a == b);

Integer c = 100000;
Integer d = 100000;
System.out.println(c.equals(d));

Integer e = 127;  //  - 128  ~  127
Integer f = 127;
System.out.println(e == f);

HashMap<Node, String> map2 = new HashMap<>();
Node node1 = new Node(1);
Node node2 = node1;
map2.put(node1, "我是node1");
map2.put(node2, "我是node1");
System.out.println(map2.size());

System.out.println("======================");

TreeMap<Integer, String> treeMap = new TreeMap<>();

treeMap.put(3, "我是3");
treeMap.put(4, "我是4");
treeMap.put(8, "我是8");
treeMap.put(5, "我是5");
treeMap.put(7, "我是7");
treeMap.put(1, "我是1");
treeMap.put(2, "我是2");

System.out.println(treeMap.containsKey(1));
System.out.println(treeMap.containsKey(10));

System.out.println(treeMap.get(4));
System.out.println(treeMap.get(10));

treeMap.put(4, "他是4");
System.out.println(treeMap.get(4));

treeMap.remove(4);
System.out.println(treeMap.get(4));

System.out.println(treeMap.firstKey());
System.out.println(treeMap.lastKey());
// <= 4
System.out.println(treeMap.floorKey(4));
// >= 4
System.out.println(treeMap.ceilingKey(4));

// O(logN)

}

}
posted @ 2020-07-12 23:54  -Inky  阅读(172)  评论(1编辑  收藏