对数据结构和算法的理解
程序=数据结构+算法
,程序本质上就是用计算机替代人工来计算数据的,其中数据结构是数据的组织方式,算法是操作数据的逻辑,而数据结构和算法最终都对程序的性能起这决定性的作用,进而决定程序在经济市场上的表现。
程序=问题/需求
,在决定使用哪种数据结构和算法时,并不是拍脑门想出来的,而是根据实际问题/需求的特点来分析,进而选择最合适的数据结构和算法,保证程序的功能和性能都能让客服满意并且付钱。
所以进一步的说,学习算法和数据结构,就是学习在特定问题下如何有效组织数据并得出结算结果的过程。
时间复杂度与空间复杂度
时间复杂度和空间复杂度是衡量一个程序的执行时间和占用内存的抽象表示方法,一般用大O来表示,相关的数学证明过程比较复杂,我等菜鸡就直接忽略吧。
常见的时间复杂度有O(1)/O(logn)/O(n)/O(nlogn)/O(n^2),通常表示的是程序所花费的计算时间和数据规模之间的函数曲线趋势。
数据结构
数据结构是待计算的数据的容器,主流的数据结构主要分为线性结构
、树形结构
和图
,其中线性结构又可分为数组、链表、栈、队列、哈希表,树形结构主要包括二叉搜索树、AVL树、红黑树、堆、B/B+树等。下面逐个来了解。
一维结构
数组
/**
* 基于数组实现一个动态列表:
* 1)初始化
* 2)size/toString
* 3) add O(n)
* 4) delete O(n)
* 5) update O(1)
* 6) get O(1)
* 7) grow
* */
public class MyArrayList implements MyList {
private int[] data;
private int size;
public MyArrayList(int capacity) {
this.data = new int[capacity];
this.size = 0;
}
public void addFirst(int val) {
add(0, val);
}
public void addLast(int val) {
add(size, val);
}
public void add(int idx, int val) {
if (idx < 0 || idx > this.size) {
throw new IllegalArgumentException("index error.");
}
if (this.size == this.data.length) {
growUp(2 * this.data.length);
}
//将index - size - 1的数据向后移动一位
for (int i = size - 1;i >= idx;i --) {
this.data[i + 1] = this.data[i];
}
//将val设置在idx的位置
this.data[idx] = val;
this.size ++ ;
}
/**
* 扩容为原来的2倍.
* */
private void growUp(int newCapacity) {
int[] newData = new int[newCapacity];
for (int i = 0;i < size;i ++) {
newData[i] = this.data[i];
}
this.data = newData;
}
public int deleteFirst() {
return delete(0);
}
public int deleteLast() {
return delete(size - 1);
}
public int delete(int idx) {
if (idx < 0 || idx >= this.size) {
throw new IllegalArgumentException("index error.");
}
int delVal = this.data[idx];
//将idx + 1 - size - 1的数据往前移动
for (int i = idx; i < this.size - 1;i ++) {
this.data[i] = this.data[i + 1];
}
this.size -= 1;
return delVal;
}
public int set(int idx, int newVal) {
if (idx < 0 || idx >= this.size) {
throw new IllegalArgumentException("index error.");
}
int oldVal = this.data[idx];
this.data[idx] = newVal;
return oldVal;
}
public int getFirst(){
return get(0);
}
public int getLast() {
return get(size - 1);
}
public int get(int idx) {
if (idx < 0 || idx >= this.size) {
throw new IllegalArgumentException("index error.");
}
return this.data[idx];
}
public int getSize() {
return this.size;
}
public int getCapacity() {
return this.data.length;
}
public String toString() {
StringBuilder stringBuilder = new StringBuilder("[ ");
if (size > 0) {
for (int i= 0;i < size;i ++) {
stringBuilder.append(String.valueOf(this.data[i]));
if (i != size - 1) {
stringBuilder.append(",");
}
}
}
stringBuilder.append(" ]");
return stringBuilder.toString();
}
public static void main(String[] args) {
/**
[ 2,1,0 ]
delVal = 2
[ 1,0 ]
[ 10,0 ]
10
* */
MyArrayList myArrayList = new MyArrayList(12);
myArrayList.add(0, 0);
myArrayList.add(0, 1);
myArrayList.add(0, 2);
System.out.println(myArrayList);
int delVal = myArrayList.delete(0);
System.out.println("delVal = " + delVal);
System.out.println(myArrayList);
myArrayList.set(0, 10);
System.out.println(myArrayList);
System.out.println(myArrayList.get(0));
System.out.println();
MyArrayList myArrayList2 = new MyArrayList(1);
myArrayList2.add(0, 0);
myArrayList2.add(1, 1);
myArrayList2.add(2, 2);
System.out.println(myArrayList2.getCapacity()); //4
}
}
链表
/**
* 基于单链表实现一个动态列表.
* 1) 初始化
* 2)size
* 3) toString
* 5) add O(1) 需要一个 O(n)的查找
* 6) delete O(1) 需要一个 O(n)的查找
* 7) update O(1) 需要一个 O(n)的查找
* 8) get O(n)
* 9) capacity 无限大,取决于内存的上限
* */
public class MyLinkedList implements MyList {
private Node dummyHead;
private int size;
public MyLinkedList() {
this.dummyHead = new Node();
this.size = 0;
}
public void addFirst(int val) {
add(0, val);
}
public void addLast(int val) {
add(size, val);
}
/**
* 在指定位置添加数据,
* 遍历到idx的前一个位置
* */
public void add(int idx, int val) {
if (idx < 0 || idx > size) {
throw new IllegalArgumentException("index error.");
}
Node cur = dummyHead;
for (int i = 0;i < idx;i ++) {
cur = cur.next;
}
//创建新的node并添加到链表中
Node newNode = new Node(val, cur.next);
cur.next = newNode;
this.size ++;
}
public int deleteFirst() {
return delete(0);
}
public int deleteLast() {
return delete(size - 1);
}
/**
* 根据索引删除
* */
public int delete(int idx) {
if (idx < 0 || idx >= size) {
throw new IllegalArgumentException("index error.");
}
//遍历到idx - 1的位置
Node cur = dummyHead;
for (int i = 0;i < idx;i ++) {
cur = cur.next;
}
Node nex = cur.next;
int delVal = nex.data;
cur.next = nex.next;
nex.next = null;
this.size -- ;
return delVal;
}
public int deleteByVal(int val) {
int idx = getIdx(val);
if (idx == -1) {
throw new IllegalArgumentException("no element exist, " + val) ;
}
return delete(idx);
}
public int set(int idx, int val) {
if (idx < 0 || idx >= size) {
throw new IllegalArgumentException("index error.");
}
Node cur = dummyHead;
for (int i = 0;i <= idx;i ++) {
cur = cur.next;
}
int oldVal = cur.data;
cur.data = val;
return oldVal;
}
public int getFirst() {
return get(0);
}
public int getLast() {
return get(size - 1);
}
public int get(int idx) {
if (idx < 0 || idx >= size) {
throw new IllegalArgumentException("index error.");
}
Node cur = dummyHead;
for (int i = 0;i <= idx;i ++) {
cur = cur.next;
}
return cur.data;
}
//根据value找到对应的索引值
public int getIdx(int val) {
int idx = -1;
int i = 0;
Node cur = dummyHead.next;
while (cur != null) {
int curVal = cur.data;
if (curVal == val) {
//return idx;
idx = i;
}
cur = cur.next;
i ++;
}
return idx;
}
public int getSize() {
return this.size;
}
@Override
public int getCapacity() {
return MyList.super.getCapacity();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[ ");
Node dataNode = dummyHead.next;
while (dataNode != null) {
int val = dataNode.data;
dataNode = dataNode.next;
if (dataNode == null) {
stringBuilder.append(String.valueOf(val));
} else {
stringBuilder.append(String.valueOf(val) + ",");
}
}
stringBuilder.append(" ]");
return stringBuilder.toString();
}
private static class Node {
public int data;
public Node next;
public Node() {}
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
}
public static void main(String[] args) {
/**
[ 1,2,3 ]
[ 1,4,2,3 ]
3
4
[ 1,2,3 ]
3
[ 1,2 ]
2
[ 1,10 ]
10
* */
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addFirst(1);
myLinkedList.addFirst(2);
myLinkedList.addFirst(3);
System.out.println(myLinkedList);
myLinkedList.add(1, 4);
System.out.println(myLinkedList);
System.out.println(myLinkedList.getIdx(3));
System.out.println(myLinkedList.delete(1));
System.out.println(myLinkedList);
System.out.println(myLinkedList.deleteByVal(3));
System.out.println(myLinkedList);
System.out.println(myLinkedList.set(1, 10));
System.out.println(myLinkedList);
System.out.println(myLinkedList.get(1));
}
}
栈
/**
* FILO
* 基于数组实现栈.
* */
public class MyStackWithArray implements MyStack {
private MyArrayList myArrayList;
public MyStackWithArray() {
this.myArrayList = new MyArrayList(16);
}
@Override
public void push(int val) {
myArrayList.addFirst(val);
}
@Override
public int pop() {
return myArrayList.deleteFirst();
}
@Override
public int peek() {
return myArrayList.getFirst();
}
public int getSize() {
return this.myArrayList.getSize();
}
public String toString() {
return myArrayList.toString();
}
public static void main(String[] args) {
/**
[ 3,2,1 ]
3
2
[ 2,1 ]
* */
MyStackWithArray stack = new MyStackWithArray();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack.peek());
System.out.println(stack);
}
}
/**
* FILO
* 基于链表实现的队列.
* */
public class MyStackWithLinkedList implements MyStack {
private MyLinkedList linkedList;
public MyStackWithLinkedList() {
this.linkedList = new MyLinkedList();
}
@Override
public void push(int val) {
linkedList.addFirst(val);
}
@Override
public int pop() {
return linkedList.deleteFirst();
}
@Override
public int peek() {
return linkedList.getFirst();
}
@Override
public int getSize() {
return linkedList.getSize();
}
public String toString() {
return linkedList.toString();
}
public static void main(String[] args) {
/**
[ 3,2,1 ]
3
2
[ 2,1 ]
* */
MyStackWithLinkedList stack = new MyStackWithLinkedList();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack.peek());
System.out.println(stack);
}
}
package echo.basic.datastructure.impl;
/**
* 双向链表,注意调整指针的顺序:先处理新的节点,再处理前后节点
* 添加、
* 修改、
* 删除、
* 查找
* 遍历
* */
public class MyTwoWayLinkedList {
private Node dummyHead;
private Node dummyTail;
private int size;
public MyTwoWayLinkedList() {
this.dummyHead = new Node();
this.dummyTail = new Node();
dummyHead.next = dummyTail;
dummyTail.pre = dummyHead;
this.size = 0;
}
//在尾部添加
public void add(int val) {
Node pre = dummyTail.pre;
Node newNode = new Node(val, pre, new Node());
pre.next = newNode;
dummyTail = newNode.next;
dummyTail.pre = newNode;
this.size ++ ;
}
public void add(int val, int idx) {
if(idx < 0 || idx >= size) {
throw new IllegalArgumentException("index error");
}
//找到idex的前一个节点
Node pre = dummyHead;
for (int i = 0;i < idx;i ++) {
pre = pre.next;
}
//创建新节点
Node node = new Node(val);
node.next = pre.next;
node.pre = pre;
//调整节点指针
pre.next.pre = node;
pre.next = node;
this.size ++ ;
}
//修改指定位置的数据
public void set(int val, int idx) {
if(idx < 0 || idx >= size) {
throw new IllegalArgumentException("index error");
}
Node cur = dummyHead;
for (int i = 0;i <= idx; i++) {
cur = cur.next;
}
cur.val = val;
}
//删除指定位置的数据
public int remove(int idx) {
if(idx < 0 || idx >= size) {
throw new IllegalArgumentException("index error");
}
Node pre = dummyHead;
for (int i = 0 ;i < idx;i ++) {
pre = pre.next;
}
Node delNode = pre.next;
delNode.next.pre = pre;
pre.next = delNode.next;
delNode.next = delNode.pre = null;
this.size -- ;
return delNode.val;
}
public int getSize() {
return size;
}
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[ ");
Node cur = dummyHead.next;
while (cur != dummyTail) {
stringBuilder.append(cur.val + " ");
cur = cur.next;
}
stringBuilder.append("]");
return stringBuilder.toString();
}
private static class Node {
public Integer val;
public Node next;
public Node pre;
public Node (Integer val, Node pre, Node next) {
this.val = val;
this.next = next;
this.pre = pre;
}
public Node(Integer val) {
this(val, null, null);
}
public Node() {
this(null);
}
}
/**
[ 1 2 5 3 ]
[ 1 10 100 2 5 3 ]
[ 1 10 200 2 5 3 ]
5
[ 1 10 200 2 3 ]
* */
public static void main(String[] args) {
MyTwoWayLinkedList linkedList = new MyTwoWayLinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(5);
linkedList.add(3);
System.out.println(linkedList);
linkedList.add(10, 1);
linkedList.add(100, 2);
System.out.println(linkedList);
linkedList.set(200, 2);
System.out.println(linkedList);
System.out.println(linkedList.remove(4));
System.out.println(linkedList);
}
}
队列
/**
* 基于数组实现的队列.
* 1) enqueue O(n)
* 2) dequeue O(1)
* */
public class MyQueueWithArray implements MyQueue {
private MyArrayList arrayList;
public MyQueueWithArray() {
arrayList = new MyArrayList(16);
}
@Override
public void enqueue(int val) {
arrayList.addFirst(val);
}
@Override
public int dequeue() {
return arrayList.deleteLast();
}
@Override
public int getSize() {
return arrayList.getSize();
}
@Override
public String toString() {
return arrayList.toString();
}
public static void main(String[] args) {
/**
[ 3,2,1 ]
1
[ 3,2 ]
* */
MyQueueWithArray queue = new MyQueueWithArray();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
System.out.println(queue);
System.out.println(queue.dequeue());
System.out.println(queue);
}
}
/**
* 基于数组的环形队列.
* 空: front = end
* 满: end + 1 = front
* 循环:front = (front + 1) % length;
* end = (end + 1) % length;
* */
public class MyCricleQueue implements MyQueue {
private int[] data;
private int size;
private int front;
private int end;
public MyCricleQueue(int capacity) {
this.front = 0;
this.end = 0;
this.data = new int[capacity + 1] ;
}
@Override
public void enqueue(int val) {
if (isFull()) {
//throw new IllegalArgumentException("queue is full.");
System.out.println("queue growing up!");
growUp(data.length * 2);
}
data[end] = val;
end = (end + 1) % data.length;
this.size ++;
}
private void growUp(int newCapacity){
int[] newData = new int[newCapacity];
int j = 0;
for (int i = front;
i != end;
i = (i + 1) % data.length, j ++) {
newData[j] = data[i];
}
this.data = newData;
this.front = 0;
this.end = j;
}
@Override
public int dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("queue is empty");
}
int frontValue = data[front];
front = (front + 1) % data.length;
this.size --;
return frontValue;
}
@Override
public int getSize() {
return size;
}
public boolean isFull() {
return (end + 1) % data.length == front;
}
public boolean isEmpty() {
return this.front == this.end;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[ ");
for (int i = front;
i != end;
i = (i + 1) % data.length) {
stringBuilder.append(data[i] + ", ");
}
stringBuilder.append(" ]");
return stringBuilder.toString();
}
public static void main(String[] args) {
/**
[ 1, 2, 3, 4, ]
1
[ 2, 3, 4, ]
* */
MyCricleQueue queue = new MyCricleQueue(2);
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(4);
System.out.println(queue);
System.out.println(queue.dequeue());
System.out.println(queue);
}
}
映射
在java中经常使用HashMap来存储具有映射关系的数据,所以有时候很容易让人想当然的认为映射就是哈希(我在刚学习相关知识的时候确实是这么认为的),但实际上,映射指的是一种具有特殊性质的存储数据的方式-即key不能重复,但并没有规定底层如何实现,可以用数组、链表、树、哈希表或其他数据结构来实现,而哈希表它实际上是一种基于哈希值的数据存储结构,java中的HashMap底层就是用哈希表来实现的。
import echo.basic.datastructure.interfaces.MyMap;
import java.util.Objects;
/**
* 基于链表的map
* */
public class MyLinkedMap<K, V> implements MyMap<K, V> {
private Node dummyHead;
private int size;
public MyLinkedMap() {
this.dummyHead = new Node();
this.size = 0;
}
@Override
public void put(K key, V value) {
Node<K, V> cur = dummyHead;
while (cur.next != null) {
if (cur.next.key.equals(key)) {
cur.next.value = value;
return;
}
cur = cur.next;
}
Node<K, V> newNode = new Node(key, value, null);
cur.next = newNode;
this.size ++;
}
@Override
public V get(K key) {
Node<K, V> node = getNode(key);
return Objects.isNull(node) ? null : node.value;
}
@Override
public V remove(K key) {
if (!isEmpty()) {
Node<K, V> cur = dummyHead;
while (cur.next != null) {
if (cur.next.key.equals(key)) {
Node<K, V> next = cur.next;
V delVal = next.value;
cur.next = next.next;
next.next = null;
this.size -- ;
return delVal;
}
cur = cur.next;
}
return null;
}
throw new IllegalArgumentException("map is empty!");
}
@Override
public boolean contains(K key) {
Node node = getNode(key);
return Objects.nonNull(node);
}
private Node<K, V> getNode(K key) {
if (!isEmpty()) {
Node<K, V> cur = dummyHead.next;
while (cur != null) {
if (cur.key.equals(key)) {
return cur;
}
cur = cur.next;
}
}
return null;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
Node cur = dummyHead.next;
while (cur != null) {
stringBuilder.append(" {" + cur.key + ":" + cur.value + "} ");
cur = cur.next;
}
stringBuilder.append("]");
return stringBuilder.toString();
}
private static class Node<K, V> {
private K key;
private V value;
private Node<K, V> next;
public Node(K key, V value, Node<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
public Node(K key) {
this(key, null, null);
}
public Node() {
this(null, null, null);
}
@Override
public String toString() {
return key + ":" + value;
}
}
public static void main(String[] args) {
/**
[ {1:1} {2:2} {3:3} ]
2
true
3
2
[ {1:1} {3:3} ]
2
* */
MyLinkedMap<String, Integer> map = new MyLinkedMap<>();
map.put("1", 1);
map.put("2", 2);
map.put("3", 3);
System.out.println(map);
System.out.println(map.get("2"));
System.out.println(map.contains("3"));
System.out.println(map.getSize());
System.out.println(map.remove("2"));
System.out.println(map);
System.out.println(map.getSize());
}
}
上面的代码是用一个链表实现了映射
,但可以看到它的效率并不高,很多操作的时间复杂度都是O(n),如果想提高效率就需要用哈希算法来实现。
哈希算法是以哈希表和哈希算法为基础,通过哈希函数把数据映射到哈希表上,而这个哈希表实际就可以理解为是一个数组,hashcode(data) % hashtable.length
是最常见的哈希函数的实现方式。
在哈希算法中还有一个必须要解决的问题:哈希冲突
,即不同的数据通过哈希函数计算出来在哈希中的位置相同。解决哈希冲突的方式主要有两种:线性探测法
和链地址法
。
线性探测法的大概思路是:在出现哈希冲突的位置,继续向前探测,直到找到为空的位置,然后把当前数据放到这个位置。线性探测法在实际使用当中用的并不多,主要是因为在在查找数据的时候时间复杂度可能会从O(1)退化成O(n)。
连地址法的大概思路是:在出现哈希冲突的位置,构建一个链表,然后将哈希值相同的数据用链表串起来,这种算法在实际使用中非常常见,比如java中的hashmap,它就是在链地址法的基础上引入了红黑树,来解决哈希冲突较多时查找数据的性能问题。
二维结构
树
二分搜索树
import echo.basic.datastructure.interfaces.MyTree;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
/**
* 二分搜索树
* 1)插入
* 2)查找
* 3)删除
* 4)遍历
* */
public class MyBinarySearchTree<T extends Comparable<T>> implements MyTree{
private Node<T> root;
private int size;
public MyBinarySearchTree() {
this.root = null;
this.size = 0;
}
//新增
public void add(T data) {
this.root = add(root, data);
}
private Node<T> add(Node<T> node, T data) {
if (node == null) {
this.size ++;
return new Node<>(data);
}
if (data.compareTo(node.data) > 0) {
node.right = add(node.right, data);
} else if (data.compareTo(node.data) < 0) {
node.left = add(node.left, data);
}
return node;
}
//查找
public boolean contains(T data) {
return Objects.nonNull(contains(root, data));
}
private Node<T> contains(Node<T> node, T data) {
if (node == null) {
return null;
}
if (node.data.equals(data)) {
return node;
} else if (data.compareTo(node.data) > 0) {
return contains(node.right, data);
} else if (data.compareTo(node.data) < 0) {
return contains(node.left, data);
}
return null;
}
//删除
//================================
//删除任意值
public void remove(T data) {
//this.root = remove(root, data);
root = remove(root, data);
}
private Node<T> remove(Node<T> node, T data) {
//终止条件:没找到,直接返回空
if (node == null) {
return null;
}
if (data.compareTo(node.data) < 0) {
node.left = remove(node.left, data);
return node;
} else if (data.compareTo(node.data) > 0) {
node.right = remove(node.right, data);
return node;
} else {
//找到了待删除 节点,分三种情况
//1. 只有左子树, 返回当前节点左子树
//2. 只有右子树,返回当前节点右子树
//3. 既有左子树 又有右子树
// 找到右子树中的最小节点
// 用这个节点替代当前节点
if (node.right == null) {
Node<T> left = node.left;
node.left = null;
size --;
return left;
}
if (node.left == null) {
Node<T> right = node.right;
node.right = null;
size -- ;
return right;
}
Node<T> minNode = getMinNode(node.right);
//将右子树的minNode提升到node的位置
minNode.right = removeMin(node.right);
minNode.left = node.left;
node.right = null;
node.left = null;
return minNode;
}
}
//删除最小值
public T removeMin() {
T min = min();
removeMin(root);
return min;
}
private Node<T> removeMin(Node<T> node) {
if (node.left == null) {
Node<T> right = node.right;
node.right = null;
this.size -- ;
return right;
}
node.left = removeMin(node.left);
return node;
}
//删除最大值
public T removeMax() {
T max = max();
removeMax(root);
return max;
}
private Node<T> removeMax(Node<T> node) {
if (node.right == null) {
Node<T> left = node.left;
node.left = null;
this.size -- ;
return left;
}
node.right = removeMax(node.right);
return node;
}
//======================================
public T min(){
return getMinNode(root).data;
}
public T max() {
return getMaxNode(root).data;
}
private Node<T> getMinNode(Node<T> node) {
if (node.left == null) {
return node;
}
return getMinNode(node.left);
}
private Node<T> getMaxNode(Node<T> node) {
if (node.right == null) {
return node;
}
return getMaxNode(node.right);
}
//前序遍历
public void preLoop() {
preLoop(root);
}
//中序遍历
public void midLoop() {
midLoop(root);
}
//后序遍历
public void postLoop() {
postLoop(root);
}
//程序遍历
public void levelLoop() {
StringBuilder stringBuilder = new StringBuilder("[");
Queue<Node<T>> queue = new LinkedList<Node<T>>();
queue.add(root);
while (!queue.isEmpty()) {
Node<T> cur = queue.poll();
//System.out.println(cur.data);
stringBuilder.append(cur.data + " ");
if (cur.left != null) {
queue.add(cur.left);
}
if (cur.right != null) {
queue.add(cur.right);
}
}
stringBuilder.append("]");
System.out.println(stringBuilder.toString());
}
private void preLoop(Node<T> node) {
if (node == null) {
return;
}
System.out.println(node.data);
preLoop(node.left);
preLoop(node.right);
}
private void midLoop(Node<T> node) {
if (node == null) {
return;
}
preLoop(node.left);
System.out.println(node.data);
preLoop(node.right);
}
private void postLoop(Node<T> node) {
if (node == null) {
return;
}
preLoop(node.left);
preLoop(node.right);
System.out.println(node.data);
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
//定义节点
private static class Node<T> {
public T data;
private Node<T> left;
private Node<T> right;
public Node() {
this(null);
}
public Node(T data) {
this(data, null, null);
}
public Node (T data, Node<T> left, Node<T> right) {
this.data = data;
this.left = left;
this.right = right;
}
}
public static void main(String[] args) {
/**
3
1
0
5
4
true
false
0
5
0
[3 1 5 4 ]
5
[3 1 4 ]
[4 1 ]
* */
MyBinarySearchTree<Integer> binarySearchTree = new MyBinarySearchTree<>();
binarySearchTree.add(3);
binarySearchTree.add(1);
binarySearchTree.add(5);
binarySearchTree.add(0);
binarySearchTree.add(4);
binarySearchTree.preLoop();
System.out.println(binarySearchTree.contains(5));
System.out.println(binarySearchTree.contains(10));
System.out.println(binarySearchTree.min());
System.out.println(binarySearchTree.max());
System.out.println(binarySearchTree.removeMin());
binarySearchTree.levelLoop();
System.out.println(binarySearchTree.removeMax());
binarySearchTree.levelLoop();
binarySearchTree.remove(3);
//System.out.println(binarySearchTree);
binarySearchTree.levelLoop();
}
}
AVL树
红黑树
堆
package echo.basic.datastructure.impl;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* 最大堆:1)每个节点的值都大于等于孩子节点,2)是一个完全二叉树。
* 用数组来存储,
* parent = i/2
* left = i * 2
* right = i * 2 + 1
* index = 0 空置
*
* 上浮-添加:将新数据添加到最末端,然后再不断与他的parent进行比较-交换,直到满足堆的定义
* 下沉-删除:将最后一个元素替换堆顶元素,并保持原堆顶数据,然后将新的堆顶数据与他的左右子节点比较-交换,直到
* 使其满足堆的定义,最后返回原堆顶数据
* */
public class MyMaxHeap {
private Integer[] data;
private int size;
public MyMaxHeap(int capacity) {
this.data = new Integer[capacity];
this.size = 0;
}
//不考虑扩容问题
public void add(int val) {
this.size ++;
this.data[size] = val;
siftUp(size);
}
private void siftUp(int idx) {
while (idx > 1 &&
parent(idx) >= 1 &&
this.data[parent(idx)] < this.data[idx]) {
//将idx 和 parent(idx) 交换位置
int temp = this.data[parent(idx)];
this.data[parent(idx)] = this.data[idx];
this.data[idx] = temp;
//将idx向上推动一个层级
idx = parent(idx);
}
}
public int remove() {
int retVal = peek();
//将堆顶数据和数组中最后一个数据调换位置
int temp = this.data[1];
this.data[1] = this.data[size];
this.data[size] = temp;
this.data[size] = null;
//删除最后一个元素
this.size -- ;
//执行下沉操作
siftDown(1);
return retVal;
}
private void siftDown(int index) {
//如果左孩子已经数组越界了,说明当前节点已经是在堆的最底层了,
// 就终止执行
while (left(index) <= size) {
int i = left(index);
//比较当前节点左右子节点的大小,并取最大的一个
if (right(index) <= size && //存在右子节点
this.data[right(index)] > this.data[i]) {
i = right(index);
}
//比较当前节点和最大的子节点的关系
//如果小于,则对调位置
//否则,下沉操作结束
if (this.data[index] > this.data[i]) {
break;
}
int temp = this.data[index];
this.data[index] = this.data[i];
this.data[i] = temp;
index = i;
}
}
//返回堆顶数据
public int peek() {
if (size == 0) {
throw new IllegalArgumentException("heap is empty!");
}
return this.data[1];
}
private int parent(int index) {
return index / 2;
}
private int left(int index) {
return 2 * index;
}
private int right(int index) {
return 2 * index + 1;
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return this.size == 0;
}
public String toString() {
return Arrays.asList(this.data)
.stream()
//.filter(Objects::nonNull)
.map(String::valueOf)
.collect(Collectors.joining(","));
}
public static void main(String[] args) {
/**
null,4,null,null,null,null,null,null,null,null,null
null,5,4,null,null,null,null,null,null,null,null
null,5,4,3,null,null,null,null,null,null,null
null,9,5,3,4,null,null,null,null,null,null
null,20,9,3,4,5,null,null,null,null,null
20
null,9,5,3,4,null,null,null,null,null,null
9
null,5,4,3,null,null,null,null,null,null,null
* */
MyMaxHeap heap = new MyMaxHeap(11);
heap.add(4);
System.out.println(heap);
heap.add(5);
System.out.println(heap);
heap.add(3);
System.out.println(heap);
heap.add(9);
System.out.println(heap);
heap.add(20);
System.out.println(heap);
System.out.println(heap.remove());
System.out.println(heap);
System.out.println(heap.remove());
System.out.println(heap);
}
}
基于堆的优先队列
package echo.basic.datastructure.impl;
import echo.basic.datastructure.interfaces.MyQueue;
/**
* 基于大顶堆,实现一个优先级队列.
* */
public class MyPriorityQueue implements MyQueue {
private MyMaxHeap myMaxHeap ;
public MyPriorityQueue() {
this.myMaxHeap = new MyMaxHeap(16);
}
@Override
public void enqueue(int val) {
myMaxHeap.add(val);
}
@Override
public int dequeue() {
return myMaxHeap.remove();
}
public int getFront() {
return myMaxHeap.peek();
}
@Override
public int getSize() {
return myMaxHeap.size();
}
@Override
public String toString() {
return myMaxHeap.toString();
}
/**
null,8,4,2,0,null,null,null,null,null,null,null,null,null,null,null
8
null,4,0,2,null,null,null,null,null,null,null,null,null,null,null,null
* */
public static void main(String[] args) {
MyPriorityQueue queue = new MyPriorityQueue();
queue.enqueue(0);
queue.enqueue(2);
queue.enqueue(4);
queue.enqueue(8);
System.out.println(queue);
System.out.println(queue.dequeue());
System.out.println(queue);
}
}
线段树
package echo.basic.datastructure.impl;
import java.util.Map;
import java.util.TreeMap;
/**
* 基于n叉树实现的字典树(前缀树).
* */
public class MyTrie {
private Node root;
private int size;
public MyTrie() {
this.root = new Node();
this.size = 0;
}
//向前缀树中添加单词
public void add(String word) {
Node cur = root;
for (int i = 0;i < word.length();i ++) {
char c = word.charAt(i);
if (cur.next.get(c) == null) {
cur.next.put(c, new Node());
}
cur = cur.next.get(c);
}
if (!cur.isWord) {
cur.isWord = true;
this.size ++ ;
}
}
//判断在前缀树中是否包含指定的单词
public boolean contains(String word) {
Node cur = root;
for (int i = 0;i < word.length();i ++) {
char c = word.charAt(i);
//如果没有找到指定的字符,那么就说明没有这个单词
if (cur.next.get(c) == null) {
return false;
}
cur = cur.next.get(c);
}
//如果当前节点的单词标识为true,就表示这是一个完整的单词
return cur.isWord;
}
public boolean isPrefix(String prefix) {
Node cur = root;
for (int i = 0;i < prefix.length();i ++) {
char c = prefix.charAt(i);
//如果没有找到指定的字符,那么就说明没有这个单词
if (cur.next.get(c) == null) {
return false;
}
cur = cur.next.get(c);
}
//不用管单词是否为结尾字符,有,即返回true
return true;
}
public int getSize() {
return size;
}
@Override
public String toString() {
Node cur = root;
StringBuilder stringBuilder = new StringBuilder();
println(cur, stringBuilder);
return stringBuilder.toString();
}
private void println(Node cur, StringBuilder stringBuilder){
if(cur == null || cur.isWord) {
stringBuilder.append(" ");
return;
}
for (Map.Entry<Character, Node> entry : cur.next.entrySet()) {
Character character = entry.getKey();
stringBuilder.append(character);
println(entry.getValue(), stringBuilder);
}
}
private static class Node {
public boolean isWord;
public TreeMap<Character, Node> next;
public Node(boolean isWord) {
this.isWord = isWord;
this.next = new TreeMap<>();
}
public Node() {
this(false);
}
}
public static void main(String[] args) {
/**
* hello peopek le
* true
* false
* */
MyTrie myTrie = new MyTrie();
myTrie.add("hello");
myTrie.add("people");
myTrie.add("peopek");
System.out.println(myTrie);
System.out.println(myTrie.contains("hello"));
System.out.println(myTrie.contains("peop"));
}
}
字典树
并查集
B/B+树
图
其他结构
算法
原则
- 时间换空间/空间换时间
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。