diff --git a/Algorithm.md b/Algorithm.md
new file mode 100644
index 0000000..4a5a11e
--- /dev/null
+++ b/Algorithm.md
@@ -0,0 +1,2178 @@
+# 数据结构和算法
+
+该文章来自[我](https://www.github.com/lisongting) 的[https://github.com/lisongting/preparation](https://github.com/lisongting/preparation) ,这个preparation是我整理的一些面试知识点,其中有些内容和图片是来自知名博客,有些内容是我自己总结的。我打算逐渐将preparation中的内容推到[该项目](https://github.com/lisongting/android-interview-questions-cn)中,供更多人学习参考。欢迎提Issue和Pull Request.
+
+进入主题,该文档包含了以下算法的Java实现:
+
+
目录
+
+* [1.表](#1)
+ * [1.1顺序表](#1.1)
+ * [1.2单链表](#1.2)
+ * [1.3双链表](#1.3)
+* [2.栈和队列](#2)
+ * [2.1顺序栈](#2.1)
+ * [2.2链式栈](#2.2)
+ * [2.3循环队列](#2.3)
+* [3.树](#3)
+ * [3.1普通二叉树](#3.1)
+ * [3.2二叉搜索树](#3.2)
+* [4.排序](#4)
+ * [4.1冒泡排序](#4.1)
+ * [4.2快速排序](#4.2)
+ * [4.3直接选择排序](#4.3)
+ * [4.4堆排序](#4.4)
+ * [4.5直接插入排序](#4.5)
+ * [4.6折半插入排序](#4.6)
+ * [4.7希尔排序](#4.7)
+ * [4.8归并排序](#4.8)
+ * [4.9基数排序](#4.9)
+ * [4.10复杂度比较](#4.10)
+* [5.查找](#5)
+ * [5.1朴素查找](#5.1)
+ * [5.2KMP](#5.2)
+
+1.表
+
+1.1顺序表
+
+```java
+import java.util.Arrays;
+
+public class SequenceList {
+ private int DEFAULT_SIZE = 16;
+ private int capacity;
+ private Object[] elementData;
+ private int size = 0;
+ public SequenceList(){
+ capacity = DEFAULT_SIZE;
+ elementData = new Object[capacity];
+ }
+ public SequenceList(T element){
+ this();
+ elementData[0] = element;
+ size ++;
+ }
+ //初始化第一个元素,以指定大小创建List
+ public SequenceList(T element,int initSize){
+ capacity = 1;
+ //把capacity设为大于initSize的数,并且为2的n次方
+ while(capacity < initSize){
+ capacity = capacity * 2;
+ }
+ elementData = new Object[capacity];
+ elementData[0] = element;
+ size++;
+ }
+ public int length(){
+ return size;
+ }
+ public T get(int index){
+ if(index<0||index>size-1){
+ throw new IndexOutOfBoundsException("线性表索引越界");
+ }
+ return (T) elementData[index];
+ }
+ public int locate(T element){
+ for(int i=0;i size){
+ throw new IndexOutOfBoundsException("线性表索引越界");
+ }
+
+ ensureCapacity(size+1);
+ size ++;
+ //把index位置以后的元素都向后移
+ for(int i=size-1;i>index;i--){
+ elementData[i] = elementData[i-1];
+ }
+ elementData[index] = element;
+ }
+
+ //删除指定下标位置的元素
+ public void delete(int index){
+ if(index < 0 ||index> size-1){
+ throw new IndexOutOfBoundsException("线性表索引越界");
+ }
+ //把从指定位置的元素都往前移动
+ for(int i = index;i capacity){
+ capacity *= 2;
+ }
+ elementData = Arrays.copyOf(elementData, capacity);
+ }
+ public static void main(String[] args){
+ SequenceList list = new SequenceList<>();
+ list.add("aaaa");
+ list.add("bbbb");
+ list.add("cccc");
+ System.out.println(list.get(2));
+ System.out.println(list.toString());
+ list.insert(1, "tttt");
+ System.out.println(list.toString());
+ list.delete(0);
+ System.out.println(list.toString());
+ System.out.println("tttt在线性表中的位置是:"+list.locate("tttt"));
+ }
+}
+```
+[回到目录](#index)
+
+1.2单链表
+
+```java
+public class LinkList {
+ //定义一个内部类
+ private class Node{
+ private T data;
+ private Node next;
+ public Node(){};
+ public Node(T data,Node next){
+ this.data = data;
+ this.next = next;
+ }
+ }
+ private Node header;
+ private Node tail;
+ private int size = 0;
+ public LinkList(){
+ //空链表
+ header = tail = null;
+ }
+ public LinkList(T element){
+ header = new Node(element,null);
+ tail = header;
+ size++;
+ }
+ public int length(){
+ return size;
+ }
+ //根据索引查找指定位置的节点
+ public Node getNodeByIndex(int index){
+ if(index < 0||index >size-1){
+ throw new IndexOutOfBoundsException("链表索引越界");
+ }
+ Node current = header;
+ for(int i=0;i < size && current!=null;i++){
+ if(i == index){
+ return current;
+ }
+ current = current.next;
+ }
+ return null;
+ }
+ //根据节点返回所在的位置
+ public int locate(T element){
+ Node current = header;
+ for(int i=0;isize){
+ throw new IndexOutOfBoundsException("链表索引越界");
+ }
+ if(header == null){
+ add(element);
+ }else{
+ if(index==0){
+ addAtHeader(element);
+ }else{
+ //获取插入节点的前一个节点
+ Node prev = getNodeByIndex(index -1);
+ //prev的next指向新节点
+ //让新节点的next指向原来prev的下一个节点
+ prev.next = new Node(element,prev.next);
+ size++;
+ }
+ }
+
+ }
+ public void add(T element){
+ if(header == null){
+ header = new Node(element,null);
+ tail = header;
+ }else{
+ Node newNode = new Node(element,null);
+ tail.next = newNode;
+ tail = newNode;
+ }
+ size ++;
+ }
+ public void addAtHeader(T element){
+ Node tmp = new Node(element,header);
+ header = tmp;
+ //如果在插入之前是空链表
+ if(tail == null){
+ tail = header;
+ }
+ size ++;
+ }
+ //[重要]删除指定索引处的元素
+ public void delete(int index){
+ if(index < 0 || index>size-1){
+ throw new IndexOutOfBoundsException("链表索引越界");
+ }
+ Node del = null;
+ //如果被删除的是头节点
+ if(index ==0){
+ header = null;
+ header = header.next;
+ }else{
+ //获取删除节点的前一个节点
+ Node prev = getNodeByIndex(index-1);
+ //获取将要被删除的节点
+ del = prev.next;
+ prev.next = del.next;
+ del = null;
+ }
+ size --;
+ }
+ public void remove(){
+ delete(size -1);
+ }
+ public boolean isEmpty(){
+ return size ==0;
+ }
+ public void clear(){
+ Node current = header;
+ for(int i=0;i list = new LinkList<>();
+ list.insert(0, "aaaa");
+ list.add("bbbb");
+ list.add("wwww");
+ System.out.println(list.toString());
+ list.addAtHeader("cccc");
+ System.out.println(list.toString());
+ System.out.println("1号位置是"+list.getNodeByIndex(1).data);
+ list.delete(1);
+ System.out.println(list.toString());
+ System.out.println("wwww在链表中的位置是:"+list.locate("wwww"));
+
+ }
+
+}
+
+```
+[回到目录](#index)
+
+
+1.3双链表
+
+```java
+package list;
+
+
+//双链表
+public class DoubleLinkList {
+
+ private class Node{
+ private T data;
+ private Node prev;
+ private Node next;
+ public Node(){};
+ public Node(T data,Node prev,Node next){
+ this.data = data;
+ this.prev = prev;
+ this.next = next;
+ }
+ }
+ private Node header;
+ private Node tail;
+ private int size = 0;
+ public DoubleLinkList(){
+ header = tail = null;
+ }
+ public DoubleLinkList(T element){
+ header = new Node(element,null,null);
+ tail = header;
+ size ++;
+ }
+ public int length(){
+ return size;
+ }
+ public boolean isEmpty(){
+ return 0 == size;
+ }
+ //获取下标为index的节点的值
+ public T get(int index){
+ return getNodeByIndex(index).data;
+ }
+ //根据索引查找节点
+ public Node getNodeByIndex(int index){
+ if(index < 0 || index > size-1){
+ throw new IndexOutOfBoundsException("链表索引越界");
+ }
+ if(index <= size/2){
+ Node current = header;
+ for(int i=0;i<=size/2 && current != null;i++){
+ if(i == index){
+ return current;
+ }
+ current = current.next;
+ }
+ }else{
+ Node current = tail;
+ for(int i=size -1;i>size/2&¤t!=null ;i++){
+ if(i == index){
+ return current;
+ }
+ current = current.prev;
+ }
+ }
+ return null;
+ }
+
+ //根据值查找索引值
+ public int locate(T element){
+ Node current = header;
+ for(int i=0;i size-1){
+ throw new IndexOutOfBoundsException("链表索引越界");
+ }
+ if(header == null){
+ add(element);
+ }else{
+ if(index ==0){
+ addAtHeader(element);
+ }else{
+ //获取插入点的前一个节点
+ Node prev = getNodeByIndex(index-1);
+ //获取当前index处的节点
+ Node next = prev.next;
+ Node newNode = new Node(element,prev,next);
+ prev.next = newNode;
+ next.prev = newNode;
+ size++;
+ }
+ }
+
+ }
+ //在末尾插入元素
+ public void add(T element){
+ if(header ==null){
+ header = new Node(element,null,null);
+ tail = header;
+ }else{
+ Node newNode = new Node(element,tail,null);
+ tail.next = newNode;
+ tail = newNode;
+ }
+ size ++;
+ }
+ public void addAtHeader(T element){
+ Node newNode = new Node(element,null,header);
+ header = newNode;
+ //如果插入之前是空链表
+ if(tail == null){
+ tail = header;
+ }
+ size++;
+ }
+ //[重要]删除指定索引处的节点
+ public void delete(int index){
+ if(index < 0 || index > size-1){
+ throw new IndexOutOfBoundsException("链表索引越界");
+ }
+ //如果要删除的是头节点
+ if(index == 0){
+ header = header.next;
+ header.prev = null;
+ }else if(index == size -1){//如果要删除尾节点
+ Node prevNode = tail.prev;
+ tail = null;
+ prevNode.next=null;
+ tail = prevNode;
+ }else{
+ //找到index位置之前的节点
+ Node prevNode = getNodeByIndex(index-1);
+ Node delNode = prevNode.next;
+ Node nextNode = delNode.next;
+ delNode =null;
+ prevNode.next = nextNode;
+ nextNode.prev = prevNode;
+ }
+ size --;
+ }
+ //删除最后一个节点
+ public void remove(){
+ delete(size-1);
+ }
+ public void clear(){
+ Node current = header;
+ for(int i=0;i list = new DoubleLinkList<>();
+ list.add("aaa");
+ list.add("bbb");
+ list.insert("www", 1);
+ System.out.println(list.toString());
+ list.addAtHeader("ccc");
+ System.out.println(list.toString());
+ System.out.println("1号位置的元素是"+list.get(1));
+ list.delete(2);
+ System.out.println("调用delete(2)之后的链表:"+list.toString());
+ list.remove();
+ System.out.println("调用remove()之后的链表:"+list.toString());
+ list.add("ttt");
+ System.out.println("插入ttt之后"+list.toString());
+ System.out.println("ttt存放的索引值是:"+list.locate("ttt"));
+ list.clear();
+ System.out.println("清空链表之后 ,再次打印:"+list.toString());
+ }
+
+}
+```
+[回到目录](#index)
+
+2.栈和队列
+
+2.1顺序栈
+
+```java
+import java.util.Arrays;
+
+public class SequenceStack {
+ private int DEFAULT_SIZE = 10;
+ private int capacity;
+ //程序每次增加的数组长度
+ private int capacityIncrement = 0;
+ private Object[] elementData;
+ private int size = 0;
+ public SequenceStack(){
+ capacity = DEFAULT_SIZE;
+ elementData = new Object[capacity];
+ };
+ //以第一个初始元素来构造顺序栈
+ public SequenceStack(T element){
+ this();
+ elementData[0] = element;
+ size ++;
+ }
+ public SequenceStack(T element,int initSize){
+ this.capacity = initSize;
+ elementData = new Object[capacity];
+ elementData[0] = element;
+ size ++;
+ }
+ //以指定数组长度以及增量来创建顺序栈
+ public SequenceStack(T element,int initSize, int increment){
+ this.capacity = initSize;
+ this.capacityIncrement = increment;
+ elementData = new Object[capacity];
+ elementData[0] = element;
+ size++;
+ }
+ public int length(){
+ return size;
+ }
+ public boolean isEmpty(){
+ return 0 == size;
+ }
+ //[重要]入栈
+ public void push(T element){
+ ensureCapacity(size+1);
+ elementData[size] = element;
+ size++;
+ }
+ //确保容量
+ public void ensureCapacity(int needCapacity){
+ if(needCapacity > capacity){
+ if(capacityIncrement > 0){
+ while(needCapacity > capacity){
+ capacity += capacityIncrement;
+ }
+ }else{
+ while(needCapacity > capacity){
+ capacity *= 2;
+ }
+ }
+ }
+ elementData = Arrays.copyOf(elementData, capacity);
+ }
+ //入栈
+ public T pop(){
+ T tmp = (T) elementData[size-1];
+ elementData[size - 1] =null;
+ size--;
+ return tmp;
+ }
+ //返回栈顶元素,但不删除
+ public T peek(){
+ return (T) elementData[size-1];
+ }
+ public void clear(){
+ for(int i=0;i stack = new SequenceStack<>();
+ stack.push("aaaa");
+ stack.push("bbbb");
+ System.out.println("此时栈顶元素是:"+stack.peek());
+ stack.push("cccc");
+ System.out.println("自底向上栈内的元素为:"+stack.toString());
+ stack.pop();
+ System.out.println("出栈后栈内的元素为:"+stack.toString());
+ stack.push("eeee");
+ stack.push("tttt");
+ System.out.println("自底向上栈内的元素为:"+stack.toString());
+ stack.clear();
+ System.out.println("清空后栈内的元素为:"+stack.toString());
+ }
+}
+
+```
+[回到目录](#index)
+
+2.2链式栈
+
+```java
+public class LinkStack {
+ private class Node{
+ private T data;
+ private Node next;//该next是往栈底指
+ public Node(){};
+ public Node(T element){
+ data = element;
+ }
+ public Node(T data,Node next){
+ this.data = data;
+ this.next = next;
+ }
+ }
+ //栈顶元素
+ private Node top;
+ private int size = 0;
+ public LinkStack(){
+ top = null;
+ };
+ public LinkStack(T element){
+ top = new Node(element,null);
+ size ++;
+ }
+ public int length(){
+ return size;
+ }
+ public boolean isEmpty(){
+ return size == 0;
+ }
+ //[重要]
+ public void push(T element){
+ Node newNode = new Node(element,top);
+ top = newNode;
+ size ++;
+ }
+ public T pop(){
+ Node oldTop = top;
+ top = null;
+ top = oldTop.next;
+ size --;
+ return oldTop.data;
+ }
+ public T peek(){
+ return top.data;
+ }
+ public void clear(){
+ Node current = top;
+ for(int i=0;i stack = new LinkStack<>();
+ stack.push("aaaa");
+ stack.push("bbbb");
+ System.out.println("此时栈顶元素是:"+stack.peek());
+ stack.push("cccc");
+ System.out.println("自顶向下栈内的元素为:"+stack.toString());
+ stack.pop();
+ System.out.println("出栈后栈内的元素为:"+stack.toString());
+ stack.push("eeee");
+ stack.push("tttt");
+ System.out.println("自顶向下栈内的元素为:"+stack.toString());
+ stack.clear();
+ System.out.println("清空后栈内的元素为:"+stack.toString());
+ }
+
+}
+
+```
+[回到目录](#index)
+
+2.3循环队列
+
+```java
+package queue;
+//循环队列
+public class LoopQueue {
+ private int DEFAULT_SIZE = 10;
+ private int capacity;
+ private Object[] elementData;
+ //保存顺序队列中元素的当前个数
+ private int front = 0;
+ private int rear = 0;
+ public LoopQueue(){
+ capacity = DEFAULT_SIZE;
+ elementData = new Object[capacity];
+ }
+ public LoopQueue(T element){
+ this();
+ elementData = new Object[capacity];
+ rear++;
+ }
+ public LoopQueue(T element, int initSize){
+ this.capacity = initSize;
+ elementData = new Object[capacity];
+ elementData[0] = element;
+ rear ++;
+ }
+ public void add(T element){
+ if(rear == front && elementData[front]!=null){
+ throw new IndexOutOfBoundsException("队列已满");
+ }
+ elementData[rear] = element;
+ rear++;
+ //如果rear已经到最大了,则掉头
+ rear = rear==capacity? 0:rear;
+ }
+ //出队列
+ public T remove(){
+ if(isEmpty()){
+ throw new IndexOutOfBoundsException("队列已满");
+ }
+ T oldValue = (T)elementData[front];
+ elementData[front] = null;
+ front++;
+ //如果front到头则掉头
+ front = front==capacity? 0:front;
+ return oldValue;
+ }
+ //获取队列首部元素,不删除
+ public T element(){
+ if(isEmpty()){
+ throw new IndexOutOfBoundsException("队列已满");
+ }
+ return (T)elementData[front];
+ }
+ public int length(){
+ if(isEmpty()){
+ return 0;
+ }
+ return rear > front ? rear - front :capacity-(front-rear);
+ }
+ public boolean isEmpty(){
+ return rear == front && elementData[rear]==null;
+ }
+ public void clear(){
+ for(int i=0;i=rear,那么有效元素为front~capacity和0到rear之间的
+ for(int i=front;i queue = new LoopQueue<>();
+ queue.add("aaaa");
+ queue.add("bbbb");
+ queue.add("cccc");
+ queue.add("dddd");
+ System.out.println(queue);
+ System.out.println("排在队首的元素是:"+queue.element());
+ queue.remove();
+ System.out.println("出队列之后:"+queue);
+ queue.add("wwww");
+ System.out.println("插入wwww之后:"+queue);
+ queue.clear();
+ System.out.println("清空队列后:"+queue);
+
+ }
+
+}
+
+```
+[回到目录](#index)
+
+3.树
+
+3.1普通二叉树
+
+```java
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+
+//二叉链表树
+public class BinaryTree {
+ class Node{
+ T data;
+ Node left;
+ Node right;
+ public Node(){};
+ public Node(T data){
+ this.data = data;
+ }
+ public Node(T data, Node left, Node right){
+ this.data = data;
+ this.left = left;
+ this.right = right;
+ }
+ }
+
+ private Node root;
+ public BinaryTree(){
+ root = new Node();
+ }
+ public BinaryTree(T data){
+ root = new Node(data,null,null);
+ }
+ //为指定节点添加子节点
+ public Node addNode(Node parent,T data,boolean isLeft){
+ if(parent == null){
+ throw new RuntimeException("parent节点为null,无法添加子节点");
+ }
+ if(isLeft && parent.left!=null){
+ throw new RuntimeException("parent已有左子节点,无法添加子节点");
+ }
+ if(!isLeft && parent.right!=null){
+ throw new RuntimeException("parent已有右子节点,无法添加子节点");
+ }
+ Node newNode = new Node(data);
+ if(isLeft){
+ parent.left = newNode;
+ }else{
+ parent.right = newNode;
+ }
+ return newNode;
+ }
+
+ public boolean isEmpty(){
+ return root.data == null;
+ }
+ //获取根节点
+ public Node root(){
+ if(isEmpty()){
+ throw new RuntimeException("树为空");
+ }
+ return root;
+ }
+ //[重要]获取树的深度
+ public int deep(Node root){
+ if(root == null){
+ return 0;
+ }else{
+ return 1+Math.max(deep(root.left),deep(root.right));
+ }
+ }
+
+ //[重要]先序遍历:先遍历根节点
+ //中序遍历和后序遍历都是同理
+ public void preVisit(){
+ List list = new ArrayList<>();
+ list = preVisit(root);
+ for(int i=0;i preVisit(Node node){
+ List tmpList = new ArrayList<>();
+ //如果是中序遍历则将这行放在中间,如果是后序遍历则放在最后
+ tmpList.add(node);
+ //递归处理左子树
+ if(node.left != null){
+ tmpList.addAll(preVisit(node.left));
+ }
+ if(node.right != null){
+ tmpList.addAll(preVisit(node.right));
+ }
+ return tmpList;
+ }
+
+ //[重要]广度优先遍历
+ public void breadthFirst(){
+ Queue queue = new ArrayDeque();
+ List list = new ArrayList<>();
+ if(root != null){
+ queue.offer(root);
+ }
+ while(!queue.isEmpty()){
+ //将根元素加入队列
+ list.add(queue.peek());
+ //从队列头部取出元素
+ Node p = queue.poll();
+ //如果左子节点不为空,则加入到队列
+ if(p.left!=null){
+ queue.offer(p.left);
+ }
+ if(p.right!=null){
+ queue.offer(p.right);
+ }
+ }
+ //输出所有节点信息
+ for(int i=0;i tree = new BinaryTree<>("A");
+ BinaryTree.Node tn1 = tree.addNode(tree.root(), "B", true);
+ BinaryTree.Node tn2 = tree.addNode(tree.root(), "C", false);
+ BinaryTree.Node tn3 = tree.addNode(tn2, "D", true);
+ BinaryTree.Node tn4 = tree.addNode(tn2, "E", false);
+ BinaryTree.Node tn5 = tree.addNode(tn3, "F", true);
+ System.out.println("C的左子节点:"+tn2.left.data);
+ System.out.println("C的右子节点:"+tn2.right.data);
+ System.out.println("D的左子节点: "+tn3.left.data);
+ System.out.println("深度:"+tree.deep(tree.root()));
+
+ System.out.print("先序遍历:");
+ tree.preVisit();
+ System.out.println();
+
+ System.out.print("广度优先遍历:");
+ tree.breadthFirst();
+ System.out.println();
+ }
+
+}
+/**
+ * 输出结果:
+ C的左子节点:D
+ C的右子节点:E
+ D的左子节点: F
+ 深度:4
+ 先序遍历:A B C D F E
+ 广度优先遍历:A B C D E F
+*/
+
+```
+[回到目录](#index)
+
+3.2二叉搜索树
+
+二叉排序树,也叫二叉搜索树,该代码构建的二叉搜索树节点存放整数。(也可以构造其他类型的二叉树,存放其他对象,只要能进行某种性质上的大小比较)根据整数大小来存放,比父节点小的总是为父节点左子节点。比父节点大的总是为父节点的右子节点。
+
+```java
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+
+public class BinarySearchTree {
+ private class Node{
+ int data;
+ Node left;
+ Node right;
+ public Node(){};
+ public Node(int element){
+ this.data = element;
+ left = right = null;
+ }
+ public Node(int element,Node left,Node right){
+ this(element);
+ this.left = left;
+ this.right = right;
+ }
+ }
+ private Node root;
+ public BinarySearchTree(){
+ root = null;
+ };
+ //以根节点创建二叉搜索树
+ public BinarySearchTree(int element){
+ root = new Node(element);
+ }
+ //[重要]插入操作
+ public void insert(int element){
+ if(root == null){
+ root = new Node(element);
+ }else{
+ Node current = root;
+ Node parent = null;
+ Node newNode = new Node(element,null,null);
+ //该变量标记新节点在左还是在右
+ boolean isLeft = false;
+ //寻找一个合适的位置作为插入点
+ while(current != null){
+ parent = current;
+ //如果新插入的节点大于当前的节点
+ if(element > current.data){
+ //则把当前节点右移
+ current = current.right;
+ isLeft = false;
+ }else{
+ //如果新节点小于等于当前节点,则把当前节点左移
+ current = current.left;
+ isLeft = true;
+ }
+ }
+ //如果是在左,则新节点放到最后一次parent的左边
+ if(isLeft){
+ parent.left = newNode;
+ }else{
+ parent.right = newNode;
+ }
+ }
+ }
+ //[重要]删除操作
+ public void remove(int element){
+ Node delNode = findNode(element);//先找到要删除的节点
+ if(delNode == null){
+ return;
+ }
+ //然后进行树结构调整操作
+ //如果要删除的节点没有左右子节点,即它本身就是叶子节点
+ if(delNode.left == null && delNode.right==null){
+ //找到父节点
+ Node parent = findParent(delNode);
+ //如果要删除的节点是左子节点
+ if(delNode == parent.left){
+ parent.left = null;
+ //这里手动将delNode设为null,如果不置为null起始也没关系
+ //反正遍历树已经访问不到它了(估计它应该会被GC回收吧)
+ delNode = null;
+ }
+ if(delNode == parent.right){
+ parent.right = null;
+ delNode = null;
+ }
+ }
+ //如果要删除的节点只有左子节点
+ else if(delNode.left!=null && delNode.right==null){
+ if(root == delNode){
+ root = root.left;
+ }else{
+ Node parent = findParent(delNode);
+ if(delNode == parent.left){
+ parent.left = delNode.left;
+ }else if(delNode == parent.right){
+ parent.right = delNode.left;
+ }
+ }
+
+ }
+ //如果要删除的节点只有右子节点
+ else if(delNode.left==null && delNode.right!=null){
+ if(delNode == root){
+ root = root.right;
+ }else{
+ Node parent = findParent(delNode);
+ if(delNode == parent.left){
+ parent.left = delNode.right;
+ }else if(delNode == parent.right){
+ parent.right = delNode.right;
+ }
+ }
+ }
+ //如果要删除的节点有左右子节点
+ else{
+ //首先在其左子树中找到最大的那个值
+ Node leftMaxNode = delNode.left;
+ while(leftMaxNode.right!=null ){
+ leftMaxNode = leftMaxNode.right;
+ }
+ //找到leftMaxNode的父节点
+ Node maxParent = findParent(leftMaxNode);
+ //找到delNode的父节点
+ Node delNodeParent = findParent(delNode);
+ //这种情况容易忽略,如果leftMaxNode的父节点刚好是要删除的节点
+ if(delNode == maxParent){
+ if(delNodeParent.left == delNode){
+ delNodeParent.left = leftMaxNode;
+ }else{
+ delNodeParent.right = leftMaxNode;
+ }
+ leftMaxNode.right = delNode.right;
+ delNode = null;
+ }else{
+ //由于leftMaxNode不可能是parent的左孩子,
+ //只可能是parent的右孩子
+ //而leftMaxNode一定没有右孩子
+ //所以将parentde右子节点指向leftMaxNode的左边(可能为null,也没关系)
+ //parent.right = null;
+ maxParent.right = leftMaxNode.left;
+ leftMaxNode.left = delNode.left;
+ leftMaxNode.right = delNode.right;
+
+ //下面还要在delNodeParent中对delNode的位置进行替换
+ //(孩子丢了,给老人家一个交代吧)
+ //如果delNode本来就是root节点
+ if(delNodeParent == null){
+ root = leftMaxNode;
+ }//如果delNode不是root节点
+ else{
+ if(delNode == delNodeParent.left){
+ //给老人家一个新儿子
+ delNodeParent.left = leftMaxNode;
+ }else{
+ delNodeParent.right = leftMaxNode;
+ }
+ }
+ delNode.left = null;
+ delNode.right = null;
+ delNode = null;
+ }
+ }
+ }
+ //根据数字查找节点
+ public Node findNode(int element){
+ if(root == null){
+ return null;
+ }
+ Node p = root;
+ while(p!=null){
+ if(element > p.data){
+ p = p.right;
+ }else if(element < p.data){
+ p = p.left;
+ }else{
+ return p;
+ }
+ }
+ return null;
+ }
+ //找到一个节点的父节点
+ public Node findParent(Node child){
+ if(child == null){
+ return null;
+ }
+ if(child == root){
+ return null;
+ }
+ Node current = root;
+ Node prev = current;
+ while(current!= null){
+ if(current.data < child.data){
+ prev = current;
+ current = current.right;
+ }else if(current.data > child.data){
+ prev = current;
+ current = current.left;
+ }else{
+
+ return prev;
+ }
+ }
+ return null;
+ }
+ //[重要]中序遍历:先遍历左子节点
+ public void middleVisit(){
+ List list = new ArrayList<>();
+ list = middleVisit(root);
+ for(int i=0;i middleVisit(Node node){
+ List tmpList = new ArrayList<>();
+ //递归处理左子树
+ if(node.left != null){
+ tmpList.addAll(middleVisit(node.left));
+ }
+ tmpList.add(node);
+ if(node.right != null){
+ tmpList.addAll(middleVisit(node.right));
+ }
+ return tmpList;
+ }
+ //[重要]广度优先遍历
+ public void breadthFirst(){
+ Queue queue = new ArrayDeque();
+ List list = new ArrayList<>();
+ if(root != null){
+ queue.offer(root);
+ }
+ while(!queue.isEmpty()){
+ //将根元素加入队列
+ list.add(queue.peek());
+ //从队列头部取出元素
+ Node p = queue.poll();
+ //如果左子节点不为空,则加入到队列
+ if(p.left!=null){
+ queue.offer(p.left);
+ }
+ if(p.right!=null){
+ queue.offer(p.right);
+ }
+ }
+ //输出所有节点信息
+ for(int i=0;i4.排序
+
+4.1冒泡排序
+
+ **冒泡排序**
+
+ * (属于交换排序)
+ * 时间复杂度:O(n*n)
+ * 空间复杂度:O(1)
+ * 稳定性:稳定
+ * 分析:最慢,效率最低的排序算法
+
+
+
+```java
+import java.util.Arrays;
+
+public class BubbleSort {
+ public static void bubbleSort(int[] array){
+ System.out.println("开始排序:");
+ for(int i=0;i array[j+1]){
+ int tmp = array[j];
+ array[j] = array[j+1];
+ array[j+1] = tmp;
+ flag = true;
+ }
+ }
+ System.out.println(Arrays.toString(array));
+ //如果某一趟中元素没有交换,则说明数组已经有序
+ //为提高效率,则直接退出
+ if(!flag){
+ break;
+ }
+ }
+ }
+ public static void main(String[] args) {
+ int[] array = new int[]{5,10,3,29,12,52,36,25};
+ System.out.println("排序前:");
+ System.out.println(Arrays.toString(array));
+ BubbleSort.bubbleSort(array);
+ System.out.println("排序后:");
+ System.out.println(Arrays.toString(array));
+ }
+ /**[运行结果]
+ * 排序前:
+ * [5, 10, 3, 29, 12, 52, 36, 25]
+ * 开始排序:
+ * [5, 3, 10, 12, 29, 36, 25, 52]
+ * [3, 5, 10, 12, 29, 25, 36, 52]
+ * [3, 5, 10, 12, 25, 29, 36, 52]
+ * [3, 5, 10, 12, 25, 29, 36, 52]
+ * 排序后:
+ * [3, 5, 10, 12, 25, 29, 36, 52]
+ */
+}
+```
+
+
+
+
+
+[回到目录](#index)
+
+
+
+4.2快速排序
+
+ **快速排序**
+
+ * (属于交换排序)
+ * 时间复杂度:最好O(nlogn) 最坏O(n*n)
+ * 空间复杂度:O(logn)
+ * 稳定性:不稳定
+ * 分析:快速排序比大部分排序算法都要快。
+ * 快速排序思路:从待排序的数据序列中任意去一个数据作为分界,所有比它小的数据元素一律放在左边,所有比它大的元素一律放在右边,这样一趟下来,形成左右两个子序列,再对这两个子序列进行相同的处理,即重新选取中心元素。进行递归处理
+
+```java
+import java.util.Arrays;
+
+public class QuickSort {
+ public static void quickSort(int[] array,int start,int end){
+ //需要排序
+ if(start < end){
+ //以第一个元素作为分界值
+ int pivot = array[start];
+ //i从左边开始搜索,查找大于分界值的元素索引
+ int i = start;
+ //j从右边开始搜索,查找小于分界值的元素索引
+ int j = end;
+ while(true){
+ while(array[j]>=pivot && i4.3直接选择排序
+
+ * 直接选择排序
+ * (属于选择排序)
+ * 时间复杂度:O(n*n)
+ * 空间复杂度:O(1)
+ * 稳定性:不稳定
+ * 分析:和冒泡排序一样效率较低
+ * 思路:第一趟从所有数据中选出最小的,放在第一位。第二趟从其他未排序位置中选出最小的,放在第二位,依次类推,一共需要进行n-1趟才能排好。
+
+```java
+import java.util.Arrays;
+
+public class SelectSort {
+ public static void selectSort(int[] array){
+ //进行n-1趟比较,第i趟比较将第i大的值选出来放在i位置上
+ for(int i=0;iarray[j]){
+ int tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+ System.out.println(Arrays.toString(array));
+ }
+ }
+ public static void main(String[] args) {
+ int[] array = new int[]{9,5,23,2,0,21,30,13};
+ System.out.println("排序前:\n"+Arrays.toString(array));
+ System.out.println("开始排序:");
+ SelectSort.selectSort(array);
+ System.out.println("排序后:\n"+Arrays.toString(array));
+ }
+ /*[运行结果]
+ 排序前:
+ [9, 5, 23, 2, 0, 21, 30, 13]
+ 开始排序:
+ [0, 9, 23, 5, 2, 21, 30, 13]
+ [0, 2, 23, 9, 5, 21, 30, 13]
+ [0, 2, 5, 23, 9, 21, 30, 13]
+ [0, 2, 5, 9, 23, 21, 30, 13]
+ [0, 2, 5, 9, 13, 23, 30, 21]
+ [0, 2, 5, 9, 13, 21, 30, 23]
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ 排序后:
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ */
+}
+```
+[回到目录](#index)
+
+
+
+4.4堆排序
+ **堆排序**
+
+ * (属于选择排序)
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ * 稳定性:不稳定
+ * 分析:通过 重建堆,从根结点开始进行从上向下的调整。每次调整都将未排序区中最大的元素放在堆顶,然后将堆顶的元素与未排序区中的最后一个元素交换,这个最大元素就进入排序区,然后再进行堆的重新调整操作。如此重复实现排序
+
+```java
+import java.util.Arrays;
+public class HeapSort {
+
+ public static void heapSort(int[] array){
+ //先把array数组构建成一个大顶堆
+ //从完全二叉树最右边的叶子节点的父节点开始
+ for(int i = array.length/2-1;i>=0;i--){
+ moveDown(array,i,array.length-1);
+ }
+ //经过上一轮的调整,已经将数组中最大的元素置于堆顶
+ //接下来进行堆排序
+ for(int i =array.length-1;i>=1;i--){
+ //将未排序的末尾元素与堆顶元素交换
+ //此时未排序区中的最大的元素会放在未排序区的最后
+ swap(array,0,i);
+ System.out.println(Arrays.toString(array));
+ //进行堆排序
+ moveDown(array,0,i-1);
+ }
+ }
+ //每一次moveDown都会把未排序区中最大的数挑出来放在堆顶
+ public static void moveDown(int[] array,int first,int last){
+ //将first的左子节点设为largest
+ int largest = first*2 +1;
+ while(largest <= last){
+ //如果first的右子节点大于左子节点
+ if(largest4.5直接插入排序
+
+ * 直接插入排序
+ * (属于插入排序)
+ * 时间复杂度:O(n*n)
+ * 空间复杂度:O(1)
+ * 稳定性:稳定
+ * 排序思路:将第一个元素看做有序区,使用游标从数组的第二个数开始扫描,然后将第二个数插入到有序区中的恰当位置,使有序区增长,游标依次后移,然后再把游标处的元素再次插入有序区
+
+```java
+import java.util.Arrays;
+
+public class InsertSort {
+
+ public static void insertSort(int[] array){
+ for(int i=1;i=0&&cursor4.6折半插入排序
+
+ * 折半插入排序
+ * (属于插入排序)
+ * 时间复杂度:O(n*n)
+ * 空间复杂度:O(1)
+ * 稳定性:稳定
+ * 分析:是直接插入排序算法的改进,每次都将有序区折半,查找存放cursor的合适位置,这样可以加快查找速度
+
+```java
+import java.util.Arrays;
+public class HalfInsertSort {
+
+ public static void halfInsertSort(int[] array){
+ for(int i=1;iarray[mid]){
+ low = mid+1;
+ }else{
+ high = mid-1;
+ }
+ }
+ //将low到i的元素整体右移一位
+ for(int j= i;j>low;j--){
+ array[j] = array[j-1];
+ }
+ //low处的元素是有序区中刚好大于等于cursor的数的位置
+ array[low] = cursor;
+ System.out.println(Arrays.toString(array)+"low:"+low);
+ }
+ }
+ public static void main(String[] args) {
+ int[] array = new int[]{9,5,23,2,0,21,30,13};
+ System.out.println("排序前:\n"+Arrays.toString(array));
+ System.out.println("开始排序:");
+ halfInsertSort(array);
+ System.out.println("排序后:\n"+Arrays.toString(array));
+ }
+ /**[运行结果]
+ 排序前:
+ [9, 5, 23, 2, 0, 21, 30, 13]
+ 开始排序:
+ [5, 9, 23, 2, 0, 21, 30, 13]low:0
+ [5, 9, 23, 2, 0, 21, 30, 13]low:2
+ [2, 5, 9, 23, 0, 21, 30, 13]low:0
+ [0, 2, 5, 9, 23, 21, 30, 13]low:0
+ [0, 2, 5, 9, 21, 23, 30, 13]low:4
+ [0, 2, 5, 9, 21, 23, 30, 13]low:6
+ [0, 2, 5, 9, 13, 21, 23, 30]low:4
+ 排序后:
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ */
+}
+
+```
+[回到目录](#index)
+
+
+
+
+4.7希尔排序
+
+ * 希尔排序
+ * (属于插入排序)
+ * 时间复杂度:平均O(nlogn)^2 最坏O(n*n)
+ * 空间复杂度:O(1)
+ * 稳定性:不稳定
+ * 分析:Shell排序是递减增量排序法:增量为gap,先比较距离较远的元素,再比较距离较近的元素,每次间隔gap的距离,并且gap逐渐递减,直接插入排序相当于增量为1的希尔排序
+
+```java
+import java.util.Arrays;
+
+public class ShellSort {
+ public static void shellSort(int[] array){
+ //gap变量保存可变增量
+ int gap = 1;
+ //由Knuth提出的经验公式:gap = gap*3+1
+ //按照3*gap+1得到增量序列的最大值
+ while(gap <= array.length/3){
+ gap = gap*3+1;
+ }
+ while(gap>0){
+ System.out.println("gap的值:"+gap);
+ for(int i=gap;i=0 && array[j]>tmp; j-=gap){
+ //将"有序区"中的元素依次向后移动
+ array[j+gap] = array[j];
+ }
+ array[j+gap] = tmp;
+ }
+ System.out.println(Arrays.toString(array));
+ }
+ //gap按照Knuth的经验公式逐渐减小
+ gap = (gap-1)/3;
+ }
+ }
+ public static void main(String[] args) {
+ int[] array = new int[]{9,5,23,2,0,21,30,13};
+ System.out.println("排序前:\n"+Arrays.toString(array));
+ System.out.println("开始排序:");
+ shellSort(array);
+ System.out.println("排序后:\n"+Arrays.toString(array));
+ }
+ /**[运行结果]
+ 排序前:
+ [9, 5, 23, 2, 0, 21, 30, 13]
+ 开始排序:
+ gap的值:4
+ [0, 5, 23, 2, 9, 21, 30, 13]
+ [0, 5, 23, 2, 9, 21, 30, 13]
+ [0, 5, 23, 2, 9, 21, 30, 13]
+ [0, 5, 23, 2, 9, 21, 30, 13]
+ gap的值:1
+ [0, 5, 23, 2, 9, 21, 30, 13]
+ [0, 5, 23, 2, 9, 21, 30, 13]
+ [0, 2, 5, 23, 9, 21, 30, 13]
+ [0, 2, 5, 9, 23, 21, 30, 13]
+ [0, 2, 5, 9, 21, 23, 30, 13]
+ [0, 2, 5, 9, 21, 23, 30, 13]
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ 排序后:
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ */
+}
+
+```
+
+[回到目录](#index)
+
+
+
+4.8归并排序
+
+ * 归并排序
+ * 时间复杂度:O(nlogn)^2
+ * 空间复杂度:O(1)
+ * 稳定性:稳定
+ * 分析:将长度为n的无序序列看成是n个长度为1的有序子序列,首先做两两合并,得到一个n/2个长度为2的有序子序列,然后再将这些有序子序列进行两两合并,不断的重复这个过程,最终得到一个长度为n的有序序列。
+
+```java
+import java.util.Arrays;
+public class MergeSort {
+
+ //left是待排序数组的第一个位置索引,right是最后一个位置索引
+ public static void mergeSort(int[] array,int left,int right){
+ if(left < right){
+ int mid = (left+right)/2;
+ mergeSort(array,left,mid);
+ mergeSort(array,mid+1,right);
+ merge(array,left,mid,right);
+ }
+ }
+ public static void merge(int[] array,int left,int center,int right){
+ //定义一个与待排序数组一样大小的临时数组
+ int[] tmpArr = new int[array.length];
+ int mid = center+1;
+ //记录中间数组的索引
+ int third = left;
+ int tmp = third;
+ while(left<=center && mid<=right){
+ //从两个数组中取出较小的值放入中间数组
+ if(array[left] < array[mid]){
+ tmpArr[third++] = array[left++];
+ }else{
+ tmpArr[third++] = array[mid++];
+ }
+ }
+ //剩余部分依次放入中间数组
+ while(mid<=right){
+ tmpArr[third++] = array[mid++];
+ }
+ while(left<=center){
+ tmpArr[third++] = array[left++];
+ }
+ //将中间数组中的内容复制到原数组
+ while(tmp<=right){
+ array[tmp] = tmpArr[tmp++];
+ }
+ System.out.println(Arrays.toString(array));
+ }
+ public static void main(String[] args) {
+ int[] array = new int[]{9,5,23,2,0,21,30,13};
+ System.out.println("排序前:\n"+Arrays.toString(array));
+ System.out.println("开始排序:");
+ mergeSort(array,0,array.length-1);
+ System.out.println("排序后:\n"+Arrays.toString(array));
+ }
+ /**[运行结果]
+ 排序前:
+ [9, 5, 23, 2, 0, 21, 30, 13]
+ 开始排序:
+ [5, 9, 23, 2, 0, 21, 30, 13]
+ [5, 9, 2, 23, 0, 21, 30, 13]
+ [2, 5, 9, 23, 0, 21, 30, 13]
+ [2, 5, 9, 23, 0, 21, 30, 13]
+ [2, 5, 9, 23, 0, 21, 13, 30]
+ [2, 5, 9, 23, 0, 13, 21, 30]
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ 排序后:
+ [0, 2, 5, 9, 13, 21, 23, 30]
+ */
+}
+
+```
+
+
+
+[回到目录](#index)
+4.9基数排序
+
+ * 基数排序
+ * 时间复杂度:平均O(k*n),k是位数个数。 最坏O(n*n)
+ * 空间复杂度:O(rd+n),r是关键码取值范围(如0-9,r是10),d是最长的位数
+ * 稳定性:稳定
+ * 分析:将待排序的数据拆分成多个关键字,然后根据子关键字
+
+ 对数据进行排序
+
+```java
+import java.util.Arrays;
+
+public class RadixSort {
+ //maxLen代表最大的数有多少位
+ public static void radixSort(int[] array,int maxLen){
+ int a = 0;//数组array下标的标记符
+ int n =1;//n=1代表比较元素的个位,n=10代表比较元素的十位
+ int m =1;//控制键值排序依据在哪一位
+ //构造一个辅助二维数组,第一维代表可能的余数,第二维用来存放
+ //数组中余数相同的元素
+ int[][] tmp = new int[10][array.length];
+ //order代表同余数组
+ //0-9,表示余数为0-9的元素的个数
+ //余数为i的有h个,则order[i]=h。
+ int[] order = new int[10];
+
+ while(m <= maxLen){
+ for(int i=0;i4.10复杂度比较
+
+
+
+[回到目录](#index)
+
+
+
+5.查找
+
+5.1朴素查找
+
+朴素匹配算法,也叫BF匹配算法或暴力匹配算法,
+将文本串一位一位地与模式串进行对比,如果中途有一位不相同,
+则将文本串的下标后移一位,再重新从模式串的第一位开始进行对比,
+该算法效率低
+
+```java
+public class NativeSearch {
+ public static int nativeSearch(String pattern,String text){
+ int plen = pattern.length();
+ int tlen = text.length();
+ int t = 0;//标记text的下标
+ if(tlen5.2KMP
+
+//朴素匹配算法的低效:当模式串与文本进行匹配时,如果出现中途有一位匹配失败,便将模式串右移一位,用模式串的第一位与原来匹配的起点的后一位进行匹配。
+
+KMP算法是找到模式串中的next数组,next数组描述的是每个字符对应的最长公共前后缀,当模式串与文本进行匹配时,当匹配到某一位不匹配时,由next数组可得知模式串应该后移的位数,从而避免像朴素匹配算法一样的低效匹配
+
+```java
+import java.util.Arrays;
+
+public class KMPSearch {
+ //下面方法用来得到模式串的next数组,next数组描述的是每个字符对应的最长公共前后缀
+ public static int[] getNext(String pattern){
+ char[] p = pattern.toCharArray();
+ int[] next = new int[p.length];
+ next[0] = -1;
+ int left = -1;//left作为前缀的索引
+ int right = 0;//right作为后缀的索引
+ //根据已知前right位推测出第right+1位
+ while(right