链表结构
- 单向链表的定义
public class Node{
public int value;
public Node next;
public Node(int data){
value = data;
}
}
- 双向链表的定义
public class DoubleNode{
public int value;
public Node last;
public Node next;
public DoubleNode(int data){
value = data;
}
}
- 单向链表和双向链表最简单的练习:链表相关的题基本上都是coding的题
- 单链表和双链表的逆序问题
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;
}
2. 删除单链表中的某个数字问题![](https://raw.githubusercontent.com/HITlittlefox/HITlittlefox.github.io/main/source/_posts/image/2022-07-05-08-49-54.png)
// head = removeValue(head, 2);
public static Node removeValue(Node head, int num) {
// head来到第一个不需要删的位置
while (head != null) {
if (head.value != num) {
break;
}
head = head.next;
}
// 1 ) head == null
// 2 ) head != null
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;
}
扩展:Java的代码也会产生内存泄露。JVM会怎样释放空间?
- 如果你现在正在生存某个变量或者正在引用某个变量,JVM不会把这个变量释放掉,
- 如果JVM找不到活着的引用的内存,JVM会把它释放(如下图所示,即便你没有把1杀死,也没有把1→2的引用去除,JVM会认为,你再怎么也无法到达1,就会自动帮助你毁灭)。
栈和队列
- 既然语言都有这些结构和api,为什么还需要手撸练习?
- 算法问题无关语言;
- 语言提供的api是有限的,当有新的功能是api不提供的,就需要改写;
- 任何软件工具的底层都是最基本的算法和数据结构,这是绕不过去的。
- 栈:先进后出
- 队列:先进先出
- 怎么实现这两种逻辑结构呢?
- 利用双链表结构实现
- 利用数组结构实现
- 栈和队列的常见面试题
1. 怎么用数组实现不超过固定大小的队列和栈?
- 栈:正常使用;
- 队列:环形数组。
// 双向链表的节点类型
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;
}
}
2. 实现一个特殊的栈,在基本功能的基础上,再实现返回栈中最小元素的功能。pop、push、getMin操作的时间复杂度都是O(1),设计的栈类型可以使用现成的栈结构。
- 两个栈,
data栈
和min栈
,每次塞入数时,data栈正常加入,min栈:塞入数和min栈顶比较,谁小加谁. - 弹出:每次同步弹出data栈和min栈
- min栈记录了每一个level情况下,最小值是什么
// 1. 两个栈, `data栈` 和 `min栈`,每次塞入数时,data栈正常加入,min栈:塞入数和min栈顶比较,谁小加谁.
// 2. 弹出:每次同步弹出data栈和min栈
// 3. min栈记录了每一个level情况下,最小值是什么
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. 两个栈, `data栈` 和 `min栈`,每次塞入数时,data栈:正常加入,min栈:塞入数>min栈顶,不压入,否则压入
2. 弹出:只有data当前要弹出的数和min栈中栈顶相同时会弹出
3. min栈记录了每一个level情况下,最小值是什么
3. 如何用栈结构实现队列结构?
4. 如何用队列结构实现栈结构?
图图
图:
- 宽度遍历:队列
- 深度遍历:栈
怎么用栈来实现图的宽度遍历
- 两个队列拼成一个栈
怎么用队列来实现图的深度遍历
- 两个栈拼成一个队列
递归行为
1. 怎么从思想上理解递归?
1. 大事分解为若干件小事,把每一个小事的答案求出来,小事的答案通过决策过程得到大事的答案
2. 怎么从实际实现的角度出发理解递归?
1. 不管是什么递归,都画出脑图
例题
求数组arr[L…R]中的最大值,怎么用递归方法实现
递归思想:
- 将[L…R]分成左右两半
- 左:[L…Mid]
- 右:[Mid+1…R]
- 递归的时间复杂度的经验公式两个条件:
- 子问题的规模一致;
- 常数项规模可以表达为O(N^d))
- 推导:a表示子问题调动了a次,N/b表示子问题的规模。
- 左部分求最大值,右部分求最大值
- [L…R]范围上的最大值,是max{左部分最大值,右部分最大值}
利用系统栈:底层就是说每次递归都会把需要的子递归放到栈顶,拿到值后,再往回走(所有递归行为都能改成非递归行为)
哈希表
- HashMap有key有value
- HashSet有key
- int double float 按值传递
- 当发现是这些数据类型时,==进行比较的是值
- Integer Double Float 按引用传递
- 当发现是这些数据类型时,==进行比较的是地址
.euqals()
进行比较的是值-128~127
进行比较的是值
- 哈希表:
- 无论遇到什么都是按值传递
- 非基础类型:引用传递
- 哈希表,增、删、改、查,在使用时,O(1)
有序表:TreeMap,时间复杂度O(log(N))
- 可以乱序塞入,但是他内部会自己排序
.firstKey()
.lastKey()
.floorKey(5)
:小于等于5,并且离5最近的.ceilingKey(5)
:大于等于5,并且离5最近的
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 963614756@qq.com。