1 Stack:栈
1.1 介绍
一种线性表,只允许在线性表的一端进行操作,即栈顶允许操作,而栈底不允许。像个桶状物,可以通过桶顶往桶里取放物品,而不能对桶底做什么,而且先放入桶中的物品总是在更底部,这就形成了栈的一个特性:先入后出。
1.2 时间复杂度
- 入栈
栈顶压入一个数据,O(1) - 出栈
移除栈顶数据,O(1)
1.3 适用场景
先入而后出的特点和递归的使用十分契合。
1.4 代码实现
- pop()
出栈方法,即弹出栈顶数据 - push()
入栈方法,即往栈顶压入一个数据 - peek()
中文探出、偷看的意思,作用是返回当前栈顶数据,即查询栈顶数据
1.4.1 Java代码
仿造JDK1.8中Stack类源码的实现,
package structure;
import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<E> {
// 用于存放栈数据的数组容器
private Object[] elementData;
// 用于标记栈中数据的个数
private int elementCount;
// 用于指定当容器需要扩容时,每次扩容的大小
private int capacityIncrement;
public MyStack() {
this(10); // 默认的初始容量大小为10
}
public MyStack(int initialCapacity) {
this(initialCapacity, 0);
}
/**
* @param initialCapacity 指定容器的初始容量大小
* @param capacityIncrement 指定容器扩容幅度
*/
public MyStack(int initialCapacity, int capacityIncrement) {
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public void push(E item) {
if (this.elementCount + 1 > this.elementData.length) { // 当容器已满时进行扩容
int oldCapacity = this.elementData.length;
// 判断是否指定了扩容的幅度,未指定则默认进行2倍扩容(同源码)
int newCapacity = oldCapacity + (capacityIncrement > 0 ? capacityIncrement : oldCapacity);
this.elementData = Arrays.copyOf(this.elementData, newCapacity);
}
this.elementData[this.elementCount++] = item;
}
public void pop() {
if (this.elementCount > 0) {
this.elementCount--;
this.elementData[this.elementCount] = null; // 垃圾回收
}
}
@SuppressWarnings("unchecked")
public E peek() {
if (this.elementCount == 0) {
throw new EmptyStackException();
}
return (E) this.elementData[this.elementCount - 1];
}
}
1.4.2 Python代码
class MyStack:
def __init__(self):
self.element_data = []
def push(self, item):
self.element_data.append(item)
def pop(self):
self.element_data.pop()
def peek(self):
return self.element_data[len(self.element_data) - 1]
1.4.3 JavaScript代码
function MyStack(elementData = []) {
this._elementData = elementData;
}
MyStack.prototype.push = function (item) {
this._elementData.push(item);
};
MyStack.prototype.pop = function () {
this._elementData.pop();
};
MyStack.prototype.peek = function () {
return this._elementData[this._elementData.length - 1];
};
2 Queue:队列
2.1 介绍
一种线性表,允许在一端存数据,另一端取数据,这也构成了他的特性:先入先出。就像排队买票,先排队的先买到票,买到票的在前面离开队伍,需要买票的则在后面加入队伍。
2.2 时间复杂度
- 入队
在最后面添加一个元素,O(1) - 出队
在最前面删除一个元素,O(1)
2.3 适用场景
根据先进先出的特点,适用于多线程阻塞队列管理。
2.4 代码实现
- add()
入队方法 - remove()
出队方发
2.4.1 Java代码
2.4.2.1 数组方式实现
package queue;
public class MyQueue {
private Object[] elementData = new Object[10];
private int elementCount;
public void add(Object item) {
this.elementData[this.elementCount++] = item;
}
public Object remove() {
if (this.elementCount == 0) {
throw new RuntimeException("the que is empty");
}
Object res = this.elementData[0];
for (int i = 0; i < this.elementCount - 1 - 1; i++) {
this.elementData[i] = this.elementData[i + 1];
}
this.elementData[this.elementCount - 1] = null;
return res;
}
}
2.4.1.2 列表方式实现
package queue;
import java.util.ArrayList;
public class MyQueue {
private ArrayList<Object> elementData = new ArrayList<>();
public void add(Object item) {
this.elementData.add(item);
}
public Object remove() {
if (this.elementData.isEmpty()) {
throw new RuntimeException("the queue is empty");
}
return this.elementData.remove(0);
}
}
2.4.1.3 两个栈组合实现
一个栈A负责入队,一个栈B负责出队,由于栈的特性先入后出,所以可以栈A中数据依次出栈、且同时入栈到栈B中,这样栈B出栈时,相当于总是移除栈A的栈底数据
package queue;
import java.util.Stack;
public class MyQueue {
private Stack<Object> inStack = new Stack<>();
private Stack<Object> outStack = new Stack<>();
public void add(Object item) {
this.inStack.push(item);
}
public Object remove() {
if (this.outStack.isEmpty()) {
while (this.inStack.size() > 0) {
this.outStack.push(this.inStack.pop());
}
}
return this.outStack.pop();
}
}
2.4.2 Python代码
class MyQueue:
def __init__(self):
self.elementData = []
def add(self, item):
self.elementData.append(item)
def remove(self):
if self.elementData.__len__() > 0:
target = self.elementData[0]
self.elementData.remove(target)
return target
2.4.3 JavaScript代码
function MyQueue(elementData=[]){
this._elementData = elementData;
}
MyQueue.prototype.add = function (item) {
this._elementData.push(item);
};
MyQueue.prototype.remove = function () {
this._elementData.shift();
};
3 Heap:堆
3.1 介绍
对于堆的定义,需满足以下条件:
- 堆是棵完全二叉树
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 称根节点值最小的堆为最小堆/小根堆;
- 称根节点值最大的堆为最大堆/大根堆
3.2 时间复杂度
O(n),以小根堆为例,具体推导过程如下:
- 树每层的节点数最多为:
2ⁿ⁻¹,n为层数 - 由1可得层数和节点数的关系为:
h = log₂n + 1,n为节点数,h为层数 - 每层及对应的推导次数关系如下(先熟悉代码后此处会更容易理解)
层数 | 最多需推导次数,h为总层数 | 节点个数 |
---|---|---|
1 | h - 1 | 2⁰ |
2 | h - 2 | 2¹ |
3 | h - 3 | 2² |
… | … | … |
h-1 | 1 | 2ʰ⁻² |
h | 0 | 2ʰ⁻¹ |
- 由3可得总的事件复杂度为:
s = (h - 1) * 2⁰ + (h - 2) * 2¹ + (h - 3) * 2² + … + (1) * 2ʰ⁻² + (0) * 2ʰ⁻¹
= (h - 1) * 2⁰ + (h - 2) * 2¹ + (h - 3) * 2² + … + (1) * 2ʰ⁻²
2s = (h - 1) * 2¹ + (h - 2) * 2² + (h - 3) * 2³ + … + (2) * 2ʰ⁻² + (1) * 2ʰ⁻¹
2s - s = - (h - 1) * 2⁰ + 2¹ + 2² + … + 2ʰ⁻² + 2ʰ⁻¹
等比数列公式:Sₙ = a₁(1 - qⁿ) / (1 - q)
2s - s = 1 - h + 2¹ * (1 - 2ʰ⁻¹) / (1 - 2)
= 2ʰ⁻¹ - h - 1
将 h = log₂n + 1 代入
2ʰ⁻¹ - h - 1 = 2n - log₂n - 2
- 取最大影响因子并去除系数,得:
O(n)
3.3 适用场景
堆排序、topN等
3.4 代码实现
- create()
创建堆方法(这里以创建最小堆为例)
3.4.1 Java代码
package structure.heap;
public class MyHeap {
/**
* 获取指定节点索引的左子节点索引
*
* @param todoNodeIndex 待处理节点索引
* @return 待处理节点的左子节点索引
*/
private int leftNodeIndex(int todoNodeIndex) {
return (todoNodeIndex + 1) * 2 - 1;
}
/**
* 获取指定节点索引的右子节点索引
*
* @param todoNodeIndex 待处理节点索引
* @return 待处理节点的右子节点索引
*/
private int rightNodeIndex(int todoNodeIndex) {
return (todoNodeIndex + 1) * 2;
}
/**
* 获取指定节点索引的父节点索引,就是求左子节点索引的反过程
*
* @param todoNodeIndex 待处理节点索引
* @return 待处理节点的父节点索引
*/
private int parentNodeIndex(int todoNodeIndex) {
return (todoNodeIndex + 1) / 2 - 1;
}
/**
* 用于创建堆
*
* 思路是,判断指定父节点中,是否有子节点值比其还小,如果有则进行一次互换,
* 以替换后的节点作为指定索引,重复前一步,直到该节点比子节点都要大时结束。
*
* 每一次判断是否互换的过程就是一次推导过程
*
* @param heap 正在构建中的堆数组
* @param todoNodeIndex 待进行推导的节点的索引
*/
@SuppressWarnings("unchecked")
private void adjust(Comparable[] heap, int todoNodeIndex) {
int leftNodeIndex = leftNodeIndex(todoNodeIndex);
int rightNodeIndex = rightNodeIndex(todoNodeIndex);
int smallestNodeValueIndex = todoNodeIndex; //先假定最小节点值对应的索引为父节点索引
if (leftNodeIndex < heap.length) { //有可能子节点的索引超出heap索引范围,如最后一层节点的子节点索引
smallestNodeValueIndex = heap[smallestNodeValueIndex].compareTo(heap[leftNodeIndex]) > 0 ? leftNodeIndex : smallestNodeValueIndex;
}
if (rightNodeIndex < heap.length) {
smallestNodeValueIndex = heap[smallestNodeValueIndex].compareTo(heap[rightNodeIndex]) > 0 ? rightNodeIndex : smallestNodeValueIndex;
}
if (smallestNodeValueIndex == todoNodeIndex) return; //当父节点节点值比子节点的节点值都要大时本次推导结束
//执行替换操作
Comparable smallNodeValue = heap[smallestNodeValueIndex];
heap[smallestNodeValueIndex] = heap[todoNodeIndex];
heap[todoNodeIndex] = smallNodeValue;
adjust(heap, smallestNodeValueIndex);// 继续推导
}
/**
* 创建堆
*
* @param arr 用于创建成堆的数组
*/
public void create(Comparable[] arr) {
if (arr.length < 2) return;
// 从后向前推导,先求最后一个有子节点的父节点索引,即最后一个索引的父节点索引
int parentNodeIndex = parentNodeIndex(arr.length - 1);
for (int i = parentNodeIndex; i >= 0; i--) {
adjust(arr, i);
}
}
}
package structure;
import org.junit.Test;
import structure.heap.MyHeap;
import java.util.Arrays;
public class MyHeapTest {
@Test
public void test(){
MyHeap heap = new MyHeap();
Comparable[] arr = {19, 5, 12, 9, 15, 16, 7, 21, 9};
heap.create(arr);
System.out.println(Arrays.toString(arr));
// [5, 9, 7, 9, 15, 16, 12, 21, 19]
}
}
3.4.2 Python代码
def get_left_node_index(todo_node_index):
return (todo_node_index + 1) * 2 - 1
def get_right_node_index(todo_node_index):
return (todo_node_index + 1) * 2
def get_parent_node_index(todo_node_index):
return (todo_node_index + 1) // 2 - 1
def adjust(todo_node_index, heap):
left_node_index = get_left_node_index(todo_node_index)
right_node_index = get_right_node_index(todo_node_index)
smallest_node_index = todo_node_index
if left_node_index < len(heap):
smallest_node_index = [smallest_node_index, left_node_index][heap[smallest_node_index] > heap[left_node_index]]
if right_node_index < len(heap):
smallest_node_index = [smallest_node_index, right_node_index][heap[smallest_node_index] > heap[right_node_index]]
if smallest_node_index == todo_node_index:
return
heap[todo_node_index], heap[smallest_node_index] = heap[smallest_node_index], heap[todo_node_index]
adjust(smallest_node_index, heap)
def create(arr):
if len(arr) < 2: return
parent_node_index = get_parent_node_index(len(arr) - 1)
for index in range(parent_node_index, -1, -1):
adjust(index, arr)
if __name__ == '__main__':
arr = [19, 5, 12, 9, 15, 16, 7, 21, 9]
create(arr)
print(arr) # [5, 9, 7, 9, 15, 16, 12, 21, 19]
3.4.3 JavaScript代码
let getLeftNodeIndex = (todoNodeIndex => (todoNodeIndex + 1) * 2 - 1);
let getRightNodeIndex = (todoNodeIndex => (todoNodeIndex + 1) * 2);
let getParentNodeIndex = (todoNodeIndex => parseInt((todoNodeIndex + 1) / 2 + "") - 1);
let adjust = ((heap, todoNodeIndex) => {
let leftNodeIndex = getLeftNodeIndex(todoNodeIndex);
let rightNodeIndex = getRightNodeIndex(todoNodeIndex);
let smallestNodeIndex = todoNodeIndex;
if (leftNodeIndex < heap.length) smallestNodeIndex = heap[smallestNodeIndex] > heap[leftNodeIndex] ? leftNodeIndex : smallestNodeIndex;
if (rightNodeIndex < heap.length) smallestNodeIndex = heap[smallestNodeIndex] > heap[rightNodeIndex] ? rightNodeIndex : smallestNodeIndex;
if (smallestNodeIndex === todoNodeIndex) return;
[heap[todoNodeIndex], heap[smallestNodeIndex]] = [heap[smallestNodeIndex], heap[todoNodeIndex]];
adjust(heap, smallestNodeIndex);
});
let create = (heap => {
let parentNodeIndex = getParentNodeIndex(heap.length - 1);
for (let i = parentNodeIndex; i >= 0; i--) {
adjust(heap, i);
}
});
let arr = [9, 5, 12, 9, 15, 16, 7, 21, 19];
create(arr);
console.log(arr); //[5, 9, 7, 9, 15, 16, 12, 21, 19]
4 BinarySearchTree:二叉查找树
4.1 介绍
二叉查找树,又称二叉搜索树,亦称二叉排序树,他有如下特点:
- 首先它是个二叉树
- 其次树中的任一节点总是大于其左子树的任一节点,小于其右子树的任一节点
- 如图:
4.2 时间复杂度
二叉查找树的时间复杂度,通常指查找节点的时间复杂度,这分为两种情况,如果这颗二叉树是平衡的,由树高和节点数的关系:h = log₂n + 1 知,最多需要比较 log₂n + 1 次,即时间复杂度为:O(log₂n),但是可能有极端情况,如正序或倒序将一批数据(如:1,2,3,4,5)依次添加构建成树,那么树的形状将是这样的:
此时树高将是 n,时间复杂度为:O(n)。
4.3 使用场景
由于二叉查找树存在最差时间复杂度:O(n)情况,所以在应用上,更多的是使用基于二叉查找树、并经过优化的平衡二叉树、红黑树等。
4.4 代码实现
- add()
添加节点方法 - search()
查找节点方法 - remove()
删除节点方法 - show()
展示所有节点,通过中序遍历方式,可以顺序输出,相当于实现了排序
4.4.1 Java代码实现
package structure.tree;
import java.util.Optional;
public class MyBinarySearchTree {
public class Node {
public Comparable value;
public Node leftNode;
public Node rightNode;
Node(Comparable value) {
this.value = value;
}
/**
* 添加节点的具体实现
* 注意这里为了方便,对于相同的节点值只进行添加一次
*
* @param node 待添加的节点
*/
@SuppressWarnings("unchecked")
private void addNode(Node node) {
if (node.value.compareTo(this.value) > 0) { //如果待添加的节点比当前节点大,说明待添加节点应该放在当前节点右子树上
if (null == this.rightNode) { //如果当前节点尚无右节点
this.rightNode = node;
} else {
this.rightNode.addNode(node);
}
} else if (node.value.compareTo(this.value) < 0) { //如果待添加的节点比当前节点小,说明待添加节点应该放在当前节点左子树上
if (null == this.leftNode) { //如果当前节点尚无左节点
this.leftNode = node;
} else {
this.leftNode.addNode(node);
}
}
}
/**
* 查找节点的具体实现
*
* @param value 待查找节点的节点值
* @return 查找到的节点,找不到则返回null
*/
@SuppressWarnings("unchecked")
private Optional<Node> searchNode(Comparable value) {
if (value.compareTo(this.value) == 0) {
return Optional.of(this);
} else if (value.compareTo(this.value) > 0 && null != this.rightNode) {
return this.rightNode.searchNode(value);
} else if (value.compareTo(this.value) < 0 && null != this.leftNode) {
return this.leftNode.searchNode(value);
}
return Optional.empty();
}
/**
* 删除节点具体实现
*
* @param node 待删除节点
*/
private void removeNode(Node node) {
Node parentNode = searchParentNode(node);//获取父节点
if (null == node.leftNode && null == node.rightNode){ //如果要删除的节点是叶子结点:没有左子节点,也没有右子节点
if (null == parentNode){ //如果删除的是根节点
MyBinarySearchTree.this.rootNode = null;
} else { //如果删除的是非根节点
if (node == parentNode.leftNode) {
parentNode.leftNode = null;
} else {
parentNode.rightNode = null;
}
}
} else if(null != node.leftNode && null != node.rightNode){ //如果要删除的节点同时有左右节点
/*
* 删除方式是:找到右子树最小节点(或左子树最大节点),删除掉,并将值赋给被删除节点
* */
Node rightTreeMinNode = searchMinNode(node.rightNode);
removeNode(rightTreeMinNode);
node.value = rightTreeMinNode.value;
} else { //如果要删除的节点存在左子节点或右子节点
if (null == parentNode){
if (null != node.leftNode){
MyBinarySearchTree.this.rootNode = MyBinarySearchTree.this.rootNode.leftNode;
} else {
MyBinarySearchTree.this.rootNode = MyBinarySearchTree.this.rootNode.rightNode;
}
} else {
if (null != node.leftNode) {
if (node == parentNode.leftNode){
parentNode.leftNode = node.leftNode;
} else {
parentNode.rightNode = node.leftNode;
}
} else {
if (node == parentNode.leftNode){
parentNode.leftNode = node.rightNode;
} else {
parentNode.rightNode = node.rightNode;
}
}
}
}
}
/**
* 查找指定节点的父节点
*
* @param node 指定节点
* @return 找到的指定节点的父节点,找不到返回null
*/
@SuppressWarnings("unchecked")
private Node searchParentNode(Node node) {
if (null == node) return null;
if ((null != this.leftNode && this.leftNode == node) || (null != this.rightNode && this.rightNode == node)) {
return this;
} else if (node.value.compareTo(this.value) < 0 && null != this.leftNode) {
return this.leftNode.searchParentNode(node);
} else if (node.value.compareTo(this.value) > 0 && null != this.rightNode) {
return this.rightNode.searchParentNode(node);
}
return null;
}
/**
* 查找指定节点的最小子节点
*
* @param node 指定节点
* @return 找到的指定节点的最小子节点
*/
private Node searchMinNode(Node node) {
if (null == node) return null;
if (null == node.leftNode) return node;
else return searchMinNode(node.leftNode);
}
/**
* 通过中序排序方式从小到大展示二叉树的所有节点值
*/
private void middleOrderShow(Node node) {
if (null == node) return;
middleOrderShow(node.leftNode);
System.out.print(node.value + " ");
middleOrderShow(node.rightNode);
}
}
/**
* 根节点
*/
private Node rootNode;
/**
* 添加节点方法
* 通过输入节点值来添加节点
*
* @param value 输入的节点值
*/
public void add(Comparable value) {
Node newNode = new Node(value);
if (null == this.rootNode) this.rootNode = newNode;
else this.rootNode.addNode(newNode);
}
/**
* 查找节点方法
* 通过输入节点值来查找对应的节点
*
* @param value 输入的节点值
* @return 查找到的节点,找不到则返回null
*/
public Node search(Comparable value) {
if (null == this.rootNode) return null;
Optional<Node> target = this.rootNode.searchNode(value);
return target.orElse(null);
}
/**
* 删除节点
* 通过输入节点值来删除对应的节点
*
* @param value 输入的节点值
*/
public void remove(Comparable value) {
if (null == this.rootNode) return;
/*先找到要删除的节点*/
Node targetNode = search(value);
if (null == targetNode) return;
this.rootNode.removeNode(targetNode);
}
/**
* 展示所有节点
*/
public void show(){
if (null == this.rootNode) return;
System.out.print("[ ");
this.rootNode.middleOrderShow(this.rootNode);
System.out.print("]");
System.out.println();
}
}
package structure;
import org.junit.Test;
import structure.tree.MyBinarySearchTree;
public class MyBinarySearchTreeTest {
@Test
public void test(){
MyBinarySearchTree tree = new MyBinarySearchTree();
Comparable[] values = {5, 2, 16, 8, 1, 19, 3, 15, 10, 21, 16, 23};
for (Comparable value : values) {
tree.add(value);
}
tree.show(); //[ 1 2 3 5 8 10 15 16 19 21 23 ]
MyBinarySearchTree.Node root = tree.search(5);
System.out.println(root.leftNode.value); //2
System.out.println(root.rightNode.value); //16
tree.remove(16);
System.out.println(root.rightNode.value); //19
tree.show(); //[ 1 2 3 5 8 10 15 19 21 23 ]
}
}
4.4.2 Python代码实现
class Node:
def __init__(self, value: int):
self.value = value
self.left_node = None
self.right_node = None
def add_node(self, node):
if node.value < self.value:
if not self.left_node:
self.left_node = node
else:
self.left_node.add_node(node)
elif node.value > self.value:
if not self.right_node:
self.right_node = node
else:
self.right_node.add_node(node)
def search_node(self, value):
if value == self.value:
return self
elif value < self.value and self.left_node:
return self.left_node.search_node(value)
elif value > self.value and self.right_node:
return self.right_node.search_node(value)
def search_parent_node(self, node):
if not node:
return
if self.left_node == node or self.right_node == node:
return self
elif node.value < self.value and self.left_node:
return self.left_node.search_parent_node(node)
elif node.value > self.value and self.right_node:
return self.right_node.search_parent_node(node)
def search_min_node(self, node):
if node:
if not node.left_node:
return node
else:
return self.search_min_node(node.left_node)
def middle_order_show(self, node):
if node:
self.middle_order_show(node.left_node)
print(node.value, end=",")
self.middle_order_show(node.right_node)
class MyBinarySearchTree:
root_node = None
def add(self, value: int):
new_node = Node(value)
if self.root_node is None:
self.root_node = new_node
else:
self.root_node.add_node(new_node)
def search(self, value: int):
if self.root_node is not Node:
return self.root_node.search_node(value)
def remove(self, value: int):
if self.root_node is Node:
return
target_node = self.root_node.search_node(value)
if not target_node:
return
parent_node = self.root_node.search_parent_node(target_node)
if not target_node.left_node and not target_node.right_node:
if not parent_node:
self.root_node = None
else:
if target_node == parent_node.left_node:
parent_node.left_node = None
else:
parent_node.right_node = None
elif target_node.left_node and target_node.right_node:
right_tree_min_node = self.root_node.search_min_node(target_node.right_node)
self.remove(right_tree_min_node.value)
target_node.value = right_tree_min_node.value
else:
if not parent_node:
if target_node.left_node:
self.root_node = target_node.left_node
else:
self.root_node = target_node.right_node
else:
if target_node.left_node:
if target_node == parent_node.left_node:
parent_node.left_node = target_node.left_node
else:
parent_node.right_node = target_node.left_node
else:
if target_node == parent_node.left_node:
parent_node.left_node = target_node.right_node
else:
parent_node.right_node = target_node.right_node
def show(self):
if self.root_node is not None:
self.root_node.middle_order_show(self.root_node)
print()
if __name__ == '__main__':
tree = MyBinarySearchTree()
for value in [5, 2, 16, 8, 1, 19, 3, 15, 10, 21, 16, 23]:
tree.add(value)
tree.show() # 1, 2, 3, 5, 8, 10, 15, 16, 19, 21, 23
root = tree.search(5)
print(root.left_node.value) # 2
print(root.right_node.value) # 16
tree.remove(16)
print(root.right_node.value) # 19
tree.show() # 1, 2, 3, 5, 8, 10, 15, 19, 21, 23
4.4.3 JavaScript代码实现
function Node(value) {
this.value = value;
this.leftNode = null;
this.rightNode = null;
}
Node.prototype.addNode = function (node) {
if (node.value < this.value) {
if (null == this.leftNode) this.leftNode = node;
else this.leftNode.addNode(node);
} else if (node.value > this.value) {
if (null == this.rightNode) this.rightNode = node;
else this.rightNode.addNode(node);
}
};
Node.prototype.searchNode = function (value) {
if (value === this.value) return this;
else if (value < this.value && this.leftNode) {
return this.leftNode.searchNode(value);
} else if (value > this.value && this.rightNode) {
return this.rightNode.searchNode(value);
} else {
return null;
}
};
Node.prototype.searchParentNode = function (node) {
if (null === node) return null;
if (node === this.leftNode || node === this.rightNode) return this;
else if (node.value < this.value && this.leftNode) return this.leftNode.searchParentNode(node);
else if (node.value > this.value && this.rightNode) return this.rightNode.searchParentNode(node);
else return null;
};
Node.prototype.searchMinNode = function (node) {
if (!node) return null;
if (null === node.leftNode) return node;
else return this.searchMinNode(node.leftNode);
};
Node.prototype.middleOrderShow = function (node) {
if (!node) return;
this.middleOrderShow(node.leftNode);
console.log(node.value);
this.middleOrderShow(node.rightNode);
};
function MyBinarySearchTree() {
this.rootNode = null;
}
MyBinarySearchTree.prototype.add = function (value) {
let newNode = new Node(value);
if (null == this.rootNode) this.rootNode = newNode;
else this.rootNode.addNode(newNode);
};
MyBinarySearchTree.prototype.search = function (value) {
if (null == this.rootNode) return;
return this.rootNode.searchNode(value);
};
MyBinarySearchTree.prototype.remove = function (value) {
if (null == this.rootNode) return;
let targetNode = this.rootNode.searchNode(value);
if (!targetNode) return;
let parentNode = this.rootNode.searchParentNode(targetNode);
if (!targetNode.leftNode && !targetNode.rightNode) {
if (!parentNode) {
this.rootNode = null;
} else {
if (targetNode === parentNode.leftNode) parentNode.leftNode = null;
else parentNode.rightNode = null;
}
} else if (targetNode.leftNode && targetNode.rightNode) {
let rightTreeMinNode = this.rootNode.searchMinNode(targetNode.rightNode);
this.remove(rightTreeMinNode.value);
targetNode.value = rightTreeMinNode.value;
} else {
if (!parentNode) {
if (targetNode.leftNode) this.rootNode = this.rootNode.leftNode;
else this.rootNode = this.rootNode.rightNode;
} else {
if (targetNode.leftNode) {
if (targetNode === parentNode.leftNode) {
parentNode.leftNode = targetNode.leftNode;
} else {
parentNode.rightNode = targetNode.leftNode;
}
} else {
if (targetNode === parentNode.leftNode) {
parentNode.leftNode = targetNode.rightNode;
} else {
parentNode.rightNode = targetNode.rightNode;
}
}
}
}
};
MyBinarySearchTree.prototype.show = function () {
if (null != this.rootNode) this.rootNode.middleOrderShow(this.rootNode)
};
let tree = new MyBinarySearchTree();
let values = [5, 2, 16, 8, 1, 19, 3, 15, 10, 21, 16, 23];
values.forEach(value => tree.add(value));
tree.show(); // 1, 2, 3, 5, 8, 10, 15, 16, 19, 21, 23
let root = tree.search(5);
console.log(root.leftNode.value); // 2
console.log(root.rightNode.value); // 16
tree.remove(16);
console.log(root.rightNode.value); // 19
tree.show(); // 1, 2, 3, 5, 8, 10, 15, 19, 21, 23
5 SinglyLinkedList:单向链表
5.1 介绍
和其他链表(还有单向循环链表、双向链表、双向循环链表)一样,单向链表也是由众多的节点组成,其中单向链表的每个节点又有两个组成部分:数据域和引用域
- 数据域
存储着具体的数据值 - 引用域
存储着下一个数据的地址,并且最后一个节点的引用域为null
5.2 时间复杂度
这个很好理解,无论添加节点、删除节点、更新节点、获取节点,都是从第一个节点开始往后找,直到直到目标,所以最坏的情况就是找到最后一个节点处,此时时间复杂度都为O(n)
5.3 代码实现
- add()
添加节点 - remove()
根据 节点值/索引 移除节点 - update()
根据 节点值/索引 更新节点 - get()
根据 指定索引 获取对应的节点 - size()
获取节点个数 - show()
遍历,展示所有节点
5.3.1 Java代码实现
package structure.linkedlist;
public class MySinglyLinkedList<E> {
class Node {
E value;
Node nextNode;
Node(E value) {
this.value = value;
}
private void addNode(Node node) {
if (null == this.nextNode) this.nextNode = node;
else this.nextNode.addNode(node);
}
private void removeNode(E value) {
Node currentNode = this.nextNode;
if (null == currentNode) return;
if (value.equals(currentNode.value)) {
this.nextNode = currentNode.nextNode;
MySinglyLinkedList.this.nodeCount--;
} else {
this.nextNode.removeNode(value);
}
}
private void removeNode(int index) {
//在此之前已经对index进行了判断
Node currentNode = this.nextNode;
int currentIndex = ++MySinglyLinkedList.this.firstNodeIndex;
if (currentIndex == index) {
this.nextNode = currentNode.nextNode;
MySinglyLinkedList.this.nodeCount--;
} else {
this.nextNode.removeNode(index);
}
}
private void updateNode(E oldValue, E newValue) {
if (oldValue.equals(this.value)) {
this.value = newValue;
} else {
if (null != this.nextNode) {
this.nextNode.updateNode(oldValue, newValue);
}
}
}
private void updateNode(int index, E newValue){
//在此之前已经对index进行了判断
int currentIndex = ++MySinglyLinkedList.this.firstNodeIndex;
if (currentIndex == index) {
this.nextNode.value = newValue;
} else {
this.nextNode.updateNode(index, newValue);
}
}
private E getNode(int index){
//在此之前已经对index进行了判断
int currentIndex = ++MySinglyLinkedList.this.firstNodeIndex;
if (currentIndex == index) {
return this.nextNode.value;
} else {
return this.nextNode.getNode(index);
}
}
private void showNodes(){
if (null != this.nextNode) {
System.out.print(this.value + ", ");
this.nextNode.showNodes();
} else {
System.out.print(this.value);
}
}
}
/**
* 第一个节点
*/
private Node firstNode;
/**
* 第一个节点的索引值
*/
private int firstNodeIndex;
/**
* 节点数
*/
private int nodeCount;
public void add(E value) {
Node newNode = new Node(value);
if (null == this.firstNode) {
this.firstNode = newNode;
} else {
this.firstNode.addNode(newNode);
}
this.nodeCount++;
}
/**
* 根据节点值来删除节点
*
* @param value 要删除的节点的节点值
*/
public void remove(E value) {
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
if (value.equals(this.firstNode.value)) {
this.firstNode = this.firstNode.nextNode;
this.nodeCount--;
} else {
this.firstNode.removeNode(value);
}
}
/**
* 根据节点索引来删除节点
*
* @param index 要删除的节点的索引
*/
public void remove(int index) {
if (index < 0 || index >= this.nodeCount) {
throw new IndexOutOfBoundsException();
}
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
if (0 == index) {
this.firstNode = this.firstNode.nextNode;
this.nodeCount--;
} else {
this.firstNodeIndex = 0;
this.firstNode.removeNode(index);
}
}
/**
* 根据节点值进行更新操作
*
* @param oldValue 旧的节点值
* @param newValue 新的节点值
*/
public void update(E oldValue, E newValue) {
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
if (oldValue.equals(this.firstNode.value)) {
this.firstNode.value = newValue;
} else {
this.firstNode.updateNode(oldValue, newValue);
}
}
/**
* 根据节点索引进行更新操作
*
* @param index 待更新节点的索引
* @param newValue 新的节点值
*/
public void update(int index, E newValue) {
if (index < 0 || index >= this.nodeCount) {
throw new IndexOutOfBoundsException();
}
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
if (0 == index) {
this.firstNode.value = newValue;
} else {
this.firstNodeIndex = 0;
this.firstNode.updateNode(index, newValue);
}
}
public E get(int index) {
if (index < 0 || index >= this.nodeCount) {
throw new IndexOutOfBoundsException();
}
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
if (0 == index) return this.firstNode.value;
else {
this.firstNodeIndex = 0;
return this.firstNode.getNode(index);
}
}
public int size() {
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
return this.nodeCount;
}
public void show() {
if (null == this.firstNode) {
throw new RuntimeException("list is empty!");
}
System.out.print("[");
this.firstNode.showNodes();
System.out.print("]");
System.out.println();
}
}
package structure;
import org.junit.Test;
import structure.linkedlist.MySinglyLinkedList;
public class MySinglyLinkedListTest {
@Test
public void test(){
MySinglyLinkedList<String> list = new MySinglyLinkedList<>();
/*1.测试添加数据*/
String[] values = {"c", "b", "a", "aa", "bb", "cc", "a", "b", "c"};
for (String value: values) {
list.add(value);
}
System.out.println(list.size()); //9
list.show(); //[c, b, a, aa, bb, cc, a, b, c]
/*2.测试删除数据*/
/*2.1.索引方式删除第一个数据*/
list.remove(0);
System.out.println(list.size()); //8
list.show(); //[b, a, aa, bb, cc, a, b, c]
/*2.2.索引方式删除非第一个数据*/
list.remove(2);
System.out.println(list.size()); //7
list.show(); //[b, a, bb, cc, a, b, c]
/*2.3.节点值方式删除第一个数据*/
list.remove("b");
System.out.println(list.size()); //6
list.show(); //[a, bb, cc, a, b, c]
/*2.4.节点值方式删除非第一个数据*/
list.remove("c");
System.out.println(list.size()); //5
list.show(); //[a, bb, cc, a, b]
/*3.测试修改数据*/
/*3.1.索引方式修改第一个数据*/
list.update(0, "aaa");
list.show(); //[aaa, bb, cc, a, b]
/*3.2.索引方式修改非第一个数据*/
list.update(2, "ccc");
list.show(); //[aaa, bb, ccc, a, b]
/*3.3.节点值方式修改第一个数据*/
list.update("aaa", "aa");
list.show(); //[aa, bb, ccc, a, b]
/*3.4.节点值方式修改非第一个数据*/
list.update("ccc", "cc");
list.show(); //[aa, bb, cc, a, b]
/*4.测试获取数据*/
/*4.1.获取第一个数据*/
System.out.println(list.get(0)); //aa
/*4.2.获取非第一个数据*/
System.out.println(list.get(2)); //cc
}
}
5.3.2 Python代码实现
class Node:
def __init__(self, value, next_node=None):
self.value = value
self.next_node = next_node
def add_node(self, node):
if not self.next_node:
self.next_node = node
else:
self.next_node.add_node(node)
def remove_node_by_value(self, value) -> int:
current_node = self.next_node
if not current_node:
return 0
if value == current_node.value:
self.next_node = current_node.next_node
return -1
else:
return self.next_node.remove_node_by_value(value)
def remove_node_by_index(self, index, first_node_index=0) -> int:
current_node = self.next_node
current_node_index = first_node_index + 1
if not current_node:
return 0
if current_node_index == index:
self.next_node = current_node.next_node
return -1
else:
return self.next_node.remove_node_by_index(index, current_node_index)
def update_node_by_value(self, old_value, new_value):
if not self.next_node:
return
if self.next_node.value == old_value:
self.next_node.value = new_value
else:
self.next_node.update_node_by_value(old_value, new_value)
def update_node_by_index(self, index, new_value, first_node_index=0):
if not self.next_node:
return
current_node_index = first_node_index + 1
if current_node_index == index:
self.next_node.value = new_value
else:
self.next_node.update_node_by_index(index, new_value, current_node_index)
def get_node(self, index, first_node_index=0):
if not self.next_node:
return
current_node_index = first_node_index + 1
if current_node_index == index:
return self.next_node.value
else:
return self.next_node.get_node(index, current_node_index)
class MySinglyLinkedList:
first_node = None
node_count = 0
def add(self, value):
new_node = Node(value)
if self.first_node is None:
self.first_node = new_node
else:
self.first_node.add_node(new_node)
self.node_count += 1
def remove_by_value(self, value):
if self.first_node is None:
raise Exception("list is empty!")
if value == self.first_node.value:
self.first_node = self.first_node.next_node
self.node_count -= 1
else:
self.node_count += self.first_node.remove_node_by_value(value)
def remove_by_index(self, index: int):
if index < 0 or index >= self.node_count:
raise IndexError('list index out of range')
if self.first_node is None:
raise Exception("list is empty!")
if 0 == index:
self.first_node = self.first_node.next_node
self.node_count -= 1
else:
self.node_count += self.first_node.remove_node_by_index(index)
def update_by_value(self, old_value, new_value):
if self.first_node is None:
raise Exception("list is empty!")
if self.first_node.value == old_value:
self.first_node.value = new_value
else:
self.first_node.update_node_by_value(old_value, new_value)
def update_by_index(self, index: int, new_value):
if index < 0 or index >= self.node_count:
raise IndexError('list index out of range')
if self.first_node is None:
raise Exception("list is empty!")
if 0 == index:
self.first_node.value = new_value
else:
self.first_node.update_node_by_index(index, new_value)
def get(self, index: int):
if index < 0 or index >= self.node_count:
raise IndexError('list index out of range')
if self.first_node is None:
raise Exception("list is empty!")
if 0 == index:
return self.first_node.value
else:
return self.first_node.get_node(index)
def size(self) -> int:
if self.first_node is None:
raise Exception("list is empty!")
return self.node_count
def show(self):
if self.first_node is None:
raise Exception("list is empty!")
current_node = self.first_node
print('[', end='')
while current_node:
print(current_node.value, end=[', ', ''][current_node.next_node is None])
current_node = current_node.next_node
print(']', end='')
print()
if __name__ == '__main__':
li = MySinglyLinkedList()
"""1.测试添加数据"""
for value in ["c", "b", "a", "aa", "bb", "cc", "a", "b", "c"]:
li.add(value)
li.show() # [c, b, a, aa, bb, cc, a, b, c]
"""2.测试删除数据"""
'''2.1.索引方式删除第一个数据'''
li.remove_by_index(0)
li.show() # [b, a, aa, bb, cc, a, b, c]
'''2.2.索引方式删除非第一个数据'''
li.remove_by_index(2)
li.show() # [b, a, bb, cc, a, b, c]
'''2.3.节点值方式删除第一个数据'''
li.remove_by_value('b')
li.show() # [a, bb, cc, a, b, c]
'''2.4.节点值方式删除非第一个数据'''
li.remove_by_value('c')
li.show() # [a, bb, cc, a, b]
"""3.测试修改数据"""
'''3.1.索引方式修改第一个数据'''
li.update_by_index(0, "aaa")
li.show() # [aaa, bb, cc, a, b]
'''3.2.索引方式修改非第一个数据'''
li.update_by_index(2, "ccc")
li.show() # [aaa, bb, ccc, a, b]
'''3.3.节点值方式修改第一个数据'''
li.update_by_value('aaa', "aa")
li.show() # [aa, bb, ccc, a, b]
'''3.4.节点值方式修改非第一个数据'''
li.update_by_value("ccc", 'cc')
li.show() # [aa, bb, cc, a, b]
"""4.测试获取数据"""
'''4.1.获取第一个索引数据'''
print(li.get(0)) # aa
'''4.2.获取非第一个索引数据'''
print(li.get(2)) # cc
5.3.3 JavaScript代码实现
function Node(value) {
this.value = value;
this.nextNode = null;
}
Node.prototype.addNode = function (node) {
if (null === this.nextNode) this.nextNode = node;
else this.nextNode.addNode(node);
};
Node.prototype.removeNodeByValue = function (value) {
let currentNode = this.nextNode;
if (null === currentNode) return 0;
if (value === currentNode.value) {
this.nextNode = currentNode.nextNode;
return -1;
} else return this.nextNode.removeNodeByValue(value);
};
Node.prototype.removeNodeByIndex = function (index, firstNodeIndex = 0) {
let currentNode = this.nextNode;
let currentNodeIndex = ++firstNodeIndex;
if (null === currentNode) return 0;
if (currentNodeIndex === index) {
this.nextNode = currentNode.nextNode;
return -1;
} else return this.nextNode.removeNodeByIndex(index, currentNodeIndex);
};
Node.prototype.updateNodeByValue = function (oldValue, newValue) {
if (this.nextNode) {
if (oldValue === this.nextNode.value) this.nextNode.value = newValue;
else this.nextNode.updateNodeByValue(oldValue, newValue);
}
};
Node.prototype.updateNodeByIndex = function (index, newValue, firstNodeIndex = 0) {
if (this.nextNode) {
let currentNodeIndex = ++firstNodeIndex;
if (currentNodeIndex === index) this.nextNode.value = newValue;
else this.nextNode.updateNodeByIndex(index, newValue, currentNodeIndex);
}
};
Node.prototype.getNode = function (index, firstNodeIndex = 0) {
if (this.nextNode) {
let currentNodeIndex = ++firstNodeIndex;
if (currentNodeIndex === index) return this.nextNode.value;
else return this.nextNode.getNode(index, currentNodeIndex);
}
};
function MySinglyLinkedList() {
this.firstNode = null;
this.nodeCount = 0;
}
MySinglyLinkedList.prototype.add = function (value) {
let newNode = new Node(value);
if (!this.firstNode) this.firstNode = newNode;
else this.firstNode.addNode(newNode);
this.nodeCount++;
};
MySinglyLinkedList.prototype.removeByValue = function (value) {
if (this.firstNode) {
if (value === this.firstNode.value) {
this.firstNode = this.firstNode.nextNode;
this.nodeCount--;
} else this.nodeCount += this.firstNode.removeNodeByValue(value);
}
};
MySinglyLinkedList.prototype.removeByIndex = function (index) {
if (this.firstNode && 0 <= index && index < this.nodeCount) {
if (0 === index) {
this.firstNode = this.firstNode.nextNode;
this.nodeCount--;
} else this.nodeCount += this.firstNode.removeNodeByIndex(index);
}
};
MySinglyLinkedList.prototype.updateByValue = function (oldValue, newValue) {
if (this.firstNode) {
if (oldValue === this.firstNode.value) this.firstNode.value = newValue;
else this.firstNode.updateNodeByValue(oldValue, newValue);
}
};
MySinglyLinkedList.prototype.updateByIndex = function (index, newValue) {
if (this.firstNode && 0 <= index && index < this.nodeCount) {
if (0 === index) this.firstNode.value = newValue;
else this.firstNode.updateNodeByIndex(index, newValue);
}
};
MySinglyLinkedList.prototype.get = function (index) {
if (this.firstNode && 0 <= index && index < this.nodeCount) {
if (0 === index) return this.firstNode.value;
else return this.firstNode.getNode(index);
}
};
MySinglyLinkedList.prototype.size = function () {
return this.nodeCount;
};
MySinglyLinkedList.prototype.show = function () {
let values = '[';
let currentNode = this.firstNode;
while (currentNode) {
values += currentNode.value;
currentNode = currentNode.nextNode;
values += currentNode ? ', ' : '';
}
values += ']';
console.log(values)
};
let list = new MySinglyLinkedList();
/*1.测试添加数据*/
["c", "b", "a", "aa", "bb", "cc", "a", "b", "c"].forEach(value => list.add(value));
list.show(); //[c, b, a, aa, bb, cc, a, b, c]
/*2.测试删除数据*/
/*2.1.索引方式删除第一个数据*/
list.removeByIndex(0);
list.show(); //[b, a, aa, bb, cc, a, b, c]
/*2.2.索引方式删除非第一个数据*/
list.removeByIndex(2);
list.show(); //[b, a, bb, cc, a, b, c]
/*2.3.节点值方式删除第一个数据*/
list.removeByValue("b");
list.show(); //[a, bb, cc, a, b, c]
/*2.4.节点值方式删除非第一个数据*/
list.removeByValue("c");
list.show(); //[a, bb, cc, a, b]
/*3.测试修改数据*/
/*3.1.索引方式修改第一个数据*/
list.updateByIndex(0, "aaa");
list.show(); //[aaa, bb, cc, a, b]
/*3.2.索引方式修改非第一个数据*/
list.updateByIndex(2, "ccc");
list.show(); //[aaa, bb, ccc, a, b]
/*3.3.节点值方式修改第一个数据*/
list.updateByValue("aaa", "aa");
list.show(); //[aa, bb, ccc, a, b]
/*3.4.节点值方式修改非第一个数据*/
list.updateByValue("ccc", "cc");
list.show(); //[aa, bb, cc, a, b]
/*4.测试获取数据*/
/*4.1.获取第一个数据*/
console.log(list.get(0)); //aa
/*4.2.获取非第一个数据*/
console.log(list.get(2)); //cc