├── Array ├── C │ └── 数组越界问题(C语言版).md └── JAVA │ ├── 数组越界问题代码实现.md │ ├── 两个有序数组的合并.md │ ├── DynamicDilatationArray.md │ ├── FixedOrderArray.md │ └── Array.md ├── 字符串匹配算法 └── JavaScript │ └── BF算法.md ├── Link_List ├── javascript │ ├── 单链表从尾到头打印.md │ ├── CircularLinkedList.md │ ├── 链表环的检测.md │ ├── SinglyLinkedList.md │ ├── 求链表的中间结点.md │ ├── 反转链表.md │ ├── 删除倒数第 K 个结点.md │ ├── DoubleLinkedList.md │ └── 两个有序链表的合并.md └── JAVA │ ├── LinkedListAlgo07.md │ └── SinglyLinkedList06.md ├── Sorts ├── JavaScript │ ├── ShellSort.md │ ├── MergeSort.md │ ├── 求第K大元素.md │ ├── QuickSort.md │ └── sorts.md └── JAVA │ ├── QuickSort.md │ ├── MergeSort.md │ └── sorts.md ├── Stack └── JAVA │ ├── ArrayStack.md │ ├── StackBasedLinkedList.md │ └── SampleBrower.md ├── Queue └── JAVA │ ├── CircularQueue.md │ └── ArrayQueue.md ├── 回溯算法 └── javascript │ └── 八皇后问题.md ├── Map └── Java │ ├── BearthFirstSearch.md │ └── DepthFirstSearch.md ├── 动态规划 └── 满减活动源码 │ └── 满减活动源码.md ├── Trie ├── JAVA │ └── Trie 字典树.md └── JavaScript │ └── Trie.md ├── Heap ├── JavaScript │ └── HeapSort.md └── JAVA │ ├── HeapSort.md │ └── Heap.md ├── Find ├── Java │ └── BinarySearch.md └── JavaScript │ └── binarySearch.md ├── 查询IP地址归属地 └── 查询IP地址归属地.md ├── Tree ├── JAVA │ └── 二叉查找树.md └── JavaScript │ └── 二叉查找树.md └── README.md /Array/C/数组越界问题(C语言版).md: -------------------------------------------------------------------------------- 1 | ```c 2 | /* 3 | * 公众号:「一个不甘平凡的码农」 4 | * 功能:检测数组边界问题 5 | * 作者:小鹿 6 | **/ 7 | #include 8 | int main() 9 | { 10 | //声明函数 11 | int array_border(); 12 | 13 | //调用函数 14 | array_border(); 15 | return 0; 16 | } 17 | 18 | //数组越界检测 19 | int array_border(){ 20 | int i = 0; 21 | int arr[3] = {0}; 22 | for(; i<=3; i++){ 23 | arr[i] = 0; 24 | printf("hello world\n"); 25 | } 26 | return 0; 27 | } 28 | 29 | //代码运行结果 30 | //出现无限循环打印 hello world 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /Array/JAVA/数组越界问题代码实现.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * 功能:数组越界问题验证 5 | * @author 小鹿 6 | */ 7 | public class Array_crossborder { 8 | 9 | public static void main(String[] args) { 10 | // TODO Auto-generated method stub 11 | medthod(); 12 | } 13 | 14 | //数组访问越界 15 | public static int medthod(){ 16 | int i = 0; 17 | int[] arr = new int[3]; 18 | try { 19 | for(; i<=3; i++){ 20 | arr[i] = 0; 21 | System.out.println("hello world"); 22 | } 23 | } catch (Exception e) { 24 | System.out.print("数组越界"); 25 | } 26 | return 0; 27 | } 28 | } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /字符串匹配算法/JavaScript/BF算法.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | const bf = (subString,patternString)=>{ 3 | // 字符串转化为数组 4 | subString = [...subString]; 5 | patternString = [...patternString]; 6 | // 子串和模式串的长度 7 | let n = subString.length; 8 | let m = patternString.length; 9 | 10 | // 进行暴力匹配 11 | for(let i = 0;i <= n - m;++i){ 12 | for(let j = 0,t = i;j < m;++j,++t){ 13 | if(subString[t] == patternString[j]){ 14 | if(j === 2){ 15 | return true; 16 | } 17 | }else{ 18 | break; 19 | } 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | // 测试 26 | let str1 = "aabacfaabaacbaacaba"; 27 | let str2 = "aaa"; 28 | console.log(bf(str1,str2)); 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /Link_List/javascript/单链表从尾到头打印.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 2019/4/25 4 | * 公众号:「一个不甘平凡的码农」 5 | * @author 小鹿 6 | * 功能:从尾到头打印链表 7 | * 1)栈实现 8 | * 2)递归实现 9 | * 10 | */ 11 | //方法一:栈实现 12 | const tailToHeadOutput = (currentNode)=>{ 13 | let stack = []; 14 | //遍历链表,将数据入栈 15 | while(currentNode !== null){ 16 | stack.push(currentNode.data); 17 | currentNode = currentNode.next; 18 | } 19 | //遍历栈,数据出栈 20 | while(stack.length !== 0){ 21 | console.log(stack.pop()); 22 | } 23 | } 24 | 25 | // 方法二:递归实现 26 | // 步骤: 27 | // 1、判断是否为空链表 28 | // 2、终止条件(下一结点为空) 29 | // 3、递归打印下一结点信息 30 | const tailToHeadOutput = (head)=>{ 31 | // 判断是否空链表 32 | if(head !== null){ 33 | // 判断下一结点是否为空 34 | if(head.next !== null){ 35 | // 下一结点不为空,先输出下一结点 36 | tailToHeadOutput(head.next) 37 | } 38 | console.log(head.data); 39 | }else{ 40 | console.log("空链表"); 41 | } 42 | } 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /Sorts/JavaScript/ShellSort.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 2019/5/27 4 | * 公众号: 「一个不甘平凡的码农」 5 | * 功能:希尔排序 6 | * @author 小鹿 7 | */ 8 | function shellSort(arr) { 9 | // 数组的总长度 10 | let len =arr.length; 11 | // 初始步长为长度的一半 12 | gap = Math.floor(len/2); 13 | // 如果步长为正整数,开始排序 14 | while(gap !== 0){ 15 | // 外循环 i 控制步长 16 | for(let i = gap;i < len;i++){ 17 | // 插入排序准备分组比较 18 | let temp = arr[i]; 19 | // 步长比较的另一方 20 | let j = i - gap; 21 | // 如果后边小,则插入排序交换位置 22 | for(;j >= 0 && temp < arr[j];j -= gap){ 23 | // 将大的数据移动到后边 24 | arr[j + gap] = arr[j]; 25 | } 26 | // 小数据补充上去(注意:上方执行了j -= gap) 27 | arr[j + gap] = temp; 28 | } 29 | // 进一步缩小步长 30 | gap=Math.floor(gap/2); 31 | } 32 | return arr; 33 | } 34 | 35 | // example 36 | let arr = [8,5,6,1,7,3,2,4]; 37 | console.log(shellSort(arr)); 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /Stack/JAVA/ArrayStack.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package easy_test; 3 | 4 | /** 5 | * 功能:基于数组的顺序栈 6 | * 7 | * @author:小鹿 8 | * 9 | */ 10 | public class ArrayStack { 11 | 12 | private String[] items; // 数组 13 | private int count; // 栈中元素个数 14 | private int n; // 栈的大小 15 | 16 | // 初始化数组,申请一个大小为 n 的数组空间 17 | public ArrayStack(int n) { 18 | this.items = new String[n]; 19 | this.n = n; 20 | this.count = 0; 21 | } 22 | 23 | /** 24 | * 功能:入栈 25 | * 说明:数组入栈的入口为数组尾部 26 | * @param item :入栈数据元素 27 | * @return:是否入栈成功 28 | */ 29 | public boolean push(String item) { 30 | // 数组空间不够了,直接返回 false,入栈失败。 31 | if (count == n) return false; 32 | // 将 item 放到下标为 count 的位置 33 | items[count] = item; 34 | //数组长度+1 35 | ++count; 36 | //入栈成功 37 | return true; 38 | } 39 | 40 | /** 41 | * 功能:出栈 42 | * 43 | * @return:返回出栈元素 44 | */ 45 | public String pop() { 46 | // 栈为空,则直接返回 null 47 | if (count == 0) return null; 48 | // 返回下标为 count-1 的数组元素 49 | String tmp = items[count-1]; 50 | //数组长度-1 51 | --count; 52 | //返回出栈数据元素 53 | return tmp; 54 | } 55 | } 56 | 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /Array/JAVA/两个有序数组的合并.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | 5 | /** 6 | * 实现两个有序数组合并为一个有序数组 7 | * @author 小鹿 8 | * 9 | */ 10 | 11 | public class MergeOrder { 12 | static int[] a = new int[] {1,2,5,6}; 13 | static int[] b = new int[] {1,3,6,7,8}; 14 | static int[] c = new int[a.length+b.length]; 15 | 16 | public static void main(String[] args) { 17 | merge(a,b,c); 18 | for(int i = 0;i < c.length; i++) { 19 | System.out.print(c[i]+" "); 20 | } 21 | 22 | } 23 | 24 | /** 25 | * 合并两个有序数组 26 | * @param a 数组 a 27 | * @param b 数组 b 28 | */ 29 | public static void merge(int[] a,int[] b,int[] c) { 30 | if(a==null || b==null) { 31 | return; 32 | }else { 33 | int i = 0; 34 | int j = 0; 35 | int n = 0; 36 | while(i < a.length && j < b.length) { 37 | if(a[i]<=b[j]) { 38 | c[n] = a[i]; 39 | i++; 40 | n++; 41 | }else { 42 | c[n] = b[j]; 43 | j++; 44 | n++; 45 | } 46 | } 47 | while(i >= a.length) { 48 | if(j < b.length) { 49 | c[n] = b[j]; 50 | j++; 51 | n++; 52 | }else { 53 | break; 54 | } 55 | } 56 | while(j >= b.length) { 57 | if(i < a.length) { 58 | c[n] = a[i]; 59 | i++; 60 | n++; 61 | }else { 62 | break; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /Queue/JAVA/CircularQueue.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | /** 5 | * 公众号:「一个不甘平凡的码农」 6 | * 时间:2019/3/2 7 | * 功能:循环队列 8 | * 1)入队 9 | * 2)出队 10 | * @author 小鹿 11 | * 12 | */ 13 | public class CircularQueue { 14 | //声明一个 int 队列 15 | private int[] items; 16 | private int n; 17 | 18 | //声明头、尾指针 19 | private int head = 0; 20 | private int tail = 0; 21 | 22 | //初始化变量 23 | public CircularQueue(int capacity) { 24 | items = new int[capacity]; 25 | n = capacity; 26 | } 27 | 28 | public static void main(String[] args) { 29 | CircularQueue c = new CircularQueue(5); 30 | c.enqueue(1); 31 | c.enqueue(2); 32 | c.enqueue(3); 33 | c.enqueue(4); 34 | //循环队列有一个空闲空间,5不在队列中 35 | System.out.println(c.enqueue(5)); 36 | c.dequeue(); 37 | System.out.println(c.enqueue(5)); 38 | c.print(); 39 | } 40 | 41 | /** 42 | * 时间:2019/3/2 43 | * 功能:入队 44 | * @param data 元素 45 | */ 46 | public Boolean enqueue(int data) { 47 | //队满 48 | if((tail+1)%n == head) return false; 49 | //入队 50 | items[tail] = data; 51 | //求下一空间 52 | tail = (tail+1) % n; 53 | return true; 54 | } 55 | 56 | /** 57 | * 时间:2019/3/2 58 | * 功能:出队 59 | */ 60 | public int dequeue() { 61 | //队空 62 | if(tail == head) return -1; 63 | //出队 64 | int temp = items[head]; 65 | head = (head+1) % n; 66 | return temp; 67 | } 68 | 69 | /** 70 | * 时间:2019/3/2 71 | * 功能:打印队列 72 | */ 73 | public void print() { 74 | for (int i = 0; i < items.length; i++) { 75 | System.out.print(items[i]+" "); 76 | } 77 | } 78 | } 79 | 80 | ``` 81 | 82 | -------------------------------------------------------------------------------- /Sorts/JAVA/QuickSort.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | 5 | /** 6 | * 功能:快速排序 7 | * @author 小鹿 8 | * 9 | */ 10 | public class QuickSort { 11 | public static void main(String[] args) { 12 | QuickSort quickSort = new QuickSort(); 13 | int[] a = new int[] {4,3,7,2,9,1}; 14 | quickSort.quickSort(a, 6); 15 | for (int i = 0; i < a.length; i++) { 16 | System.out.print(a[i]+" "); 17 | } 18 | } 19 | 20 | public void quickSort(int a[],int n) { 21 | quickSortInternally(a,0,n-1); 22 | } 23 | 24 | /** 25 | * 功能:快速排序递归函数 26 | * @param a 数组 27 | * @param i 起始位置 28 | * @param j 终止位置 29 | */ 30 | private void quickSortInternally(int[] a, int p, int r) { 31 | //终止条件 32 | if(p >= r) return; 33 | 34 | //获取区分点 pivot 35 | int q = partition(a, p, r); 36 | 37 | //区分点两端开始递归 38 | quickSortInternally(a,p,q-1); 39 | quickSortInternally(a,q+1,r); 40 | } 41 | 42 | /** 43 | * 功能:获取区分点 44 | * @param a 数组 45 | * @param p 起始位置 46 | * @param r 终止位置 47 | * @return 返回区分点 48 | */ 49 | public int partition(int[] a,int p,int r) { 50 | //将最后一个元素作为区分点 51 | int pivot = a[r]; 52 | int i = p; 53 | 54 | for (int j = p; j < r; ++j) { 55 | if(a[j] < pivot) { 56 | //确保 i 永远指向第一个大于 pivot 的数 57 | if(i == j) { 58 | i++; 59 | }else { 60 | int tmp = a[i]; 61 | a[i++] = a[j]; 62 | a[j] = tmp; 63 | } 64 | } 65 | } 66 | 67 | int temp = a[i]; 68 | a[i] = a[r]; 69 | a[r] = temp; 70 | 71 | System.out.println("i=" + i); 72 | return i; 73 | } 74 | } 75 | 76 | ``` 77 | 78 | -------------------------------------------------------------------------------- /回溯算法/javascript/八皇后问题.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | var result = []; 3 | let n = 0 4 | const cal8queens = (row) =>{ 5 | // 终止条件 6 | if(row === 8){ 7 | console.log(result) 8 | n++; 9 | return; 10 | } 11 | // 每一列的判断 12 | for(let column = 0; column < 8; column++){ 13 | // 判断当前的列位置是否合适 14 | if(isOkCulomn(row,column)){ 15 | // 保存皇后的位置 16 | result[row] = column; 17 | // 对下一行寻找数据 18 | cal8queens(row + 1); 19 | } 20 | // 此循环结束后,继续遍历下一种情况,就会形成一种枚举所有可能性 21 | } 22 | } 23 | 24 | // 判断当前列是否合适 25 | const isOkCulomn = (row,column) =>{ 26 | // 设置左上角 27 | let leftcolumn = column - 1; 28 | let rightcolumn = column + 1; 29 | 30 | for(let i = row - 1;i >= 0; i--){ 31 | // 判断当前格子正上方是否有重复 32 | if(result[i] === column) return false; 33 | 34 | // 判断当前格子左上角是否有重复 35 | if(leftcolumn >= 0){ 36 | if(result[i] === leftcolumn) return false; 37 | } 38 | 39 | // 判断当前格式右上角是否有重复 40 | if(leftcolumn < 8){ 41 | if(result[i] === rightcolumn) return false; 42 | } 43 | 44 | // 继续遍历 45 | leftcolumn --; 46 | rightcolumn ++; 47 | } 48 | return true; 49 | } 50 | 51 | // 打印八皇后 52 | const print = (result)=>{ 53 | for(let i = 0;i < 8; i++){ 54 | for(let j = 0;j < 8; j++){ 55 | if(result[i] === j){ 56 | console.log('Q' + ' ') 57 | }else{ 58 | console.log('*' + ' ') 59 | } 60 | } 61 | } 62 | } 63 | 64 | // 测试 65 | cal8queens(0); 66 | console.log(n) 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /Sorts/JAVA/MergeSort.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | 5 | /** 6 | * 2019/3/15 7 | * 公众号: 「一个不甘平凡的码农」 8 | * 归并排序 9 | * @author 小鹿 10 | * 11 | */ 12 | public class MergeSort { 13 | public static void main(String[] args) { 14 | MergeSort mSort = new MergeSort(); 15 | int[] a = new int[] {4,3,7,2,9,1}; 16 | mSort.mergeSort(a,0,a.length-1); 17 | for (int i = 0; i < a.length; i++) { 18 | System.out.print(a[i]); 19 | } 20 | } 21 | 22 | /** 23 | * 时间:2019/3/15 24 | * 功能:递归分治数据 25 | * @param a 要分治的数组 26 | * @param p 数组起始位置 27 | * @param r 数组终止位置 28 | */ 29 | public void mergeSort(int[] a,int p,int r) { 30 | //终止条件 31 | if(p >= r) return; 32 | 33 | //取p到r之间的中间位置q,防止(p+r)的和超过int类型最大值 34 | int q = p + (r - p)/2; 35 | 36 | //递归分治 37 | mergeSort(a,p,q); 38 | mergeSort(a,q+1,r); 39 | 40 | //两个有序数组的合并 41 | merge(a, p, q, r); 42 | 43 | } 44 | 45 | /** 46 | * 时间:2019/3/15 47 | * 功能: 合并 mergr 48 | * @param a 要合并的数组 49 | * @param p 数组起始的下标 50 | * @param q 第二个数组起始的下标 51 | * @param r 数组结束的下标 52 | */ 53 | public void merge(int[] a,int p,int q,int r) { 54 | int i = p,j = q+1,k = 0; 55 | int[] temp = new int[r-p+1]; 56 | 57 | //两个有序数组合并 58 | while(i <= q && j <= r) { 59 | if(a[i] <= a[j]) { 60 | temp[k++] = a[i++]; 61 | }else { 62 | temp[k++] = a[j++]; 63 | } 64 | } 65 | 66 | int s = i; 67 | int e = q; 68 | if(j <= r) { 69 | s = j; 70 | e = r; 71 | } 72 | 73 | //剩余的数据添加到尾部 74 | while(s <= e) { 75 | temp[k++] = a[s++]; 76 | } 77 | 78 | //将 temp 数据搬移到原数组 79 | for (int l = 0; l <= r-p; l++) { 80 | a[p+l] = temp[l]; 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | -------------------------------------------------------------------------------- /Sorts/JavaScript/MergeSort.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 2019/3/15 4 | * 公众号: 「一个不甘平凡的码农」 5 | * 功能:归并排序 6 | * @author 小鹿 7 | */ 8 | 9 | /** 10 | * 时间:2019/3/15 11 | * 功能:递归分治 12 | * 算法思想: 13 | * 1 将数据分治 14 | * 2 终止条件 15 | * 3 取中间值 16 | * 4 递归 分解 17 | * 5 合并 18 | * @param arr 要分治的数组 19 | */ 20 | const mergeSort = (arr) => { 21 | //终止条件 22 | if(arr.length <= 1) return arr; 23 | 24 | //找中间数值(Math.floor() 返回小于或等于一个给定数字的最大整数) 25 | const middle = Math.floor(arr.length / 2); 26 | 27 | //分割数组 28 | const left = arr.slice(0,middle); 29 | const right = arr.slice(middle); 30 | 31 | //递归 分解 合并 32 | return mergeArr(mergeSort(left),mergeSort(right)) 33 | } 34 | 35 | /** 36 | * 时间:2019/3/15 37 | * 功能: 合并函数 38 | * 1)声明两个指针分别指向两个数组的第一个数据 39 | * 2)进行比较,合并两个数组 40 | * 3)将数组剩余数据追加到尾部 41 | * @param left 数组分割的左部分 42 | * @param right 数组分割的右部分 43 | */ 44 | const mergeArr = (left,right) => { 45 | let temp = []; 46 | let leftIndex = 0; 47 | let rightIndex = 0; 48 | 49 | //判断两个数组大小,插入新数组进行排序 50 | while(leftIndex < left.length && rightIndex < right.length){ 51 | if(left[leftIndex] <= right[rightIndex]){ 52 | temp.push(left[leftIndex]); 53 | leftIndex++; 54 | }else{ 55 | temp.push(right[rightIndex]); 56 | rightIndex++; 57 | } 58 | } 59 | //合并数组多余部分数据 60 | return temp.concat(left.slice(leftIndex)).concat(right.slice(rightIndex)); 61 | } 62 | 63 | //随机数据进行排序 64 | const testArr = [] 65 | let i = 0 66 | while(i < 100){ 67 | testArr.push(Math.floor(Math.random()*1000)); 68 | i++; 69 | } 70 | //打印数组 71 | const res = mergeSort(testArr); 72 | console.log(res); 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /Stack/JAVA/StackBasedLinkedList.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package easy_test; 3 | 4 | /** 5 | * 功能:基本链表的链式栈,入栈、出栈、输出栈 6 | * @author : 小鹿 7 | * 8 | */ 9 | public class StackBasedLinkedList { 10 | //定义栈顶指针 11 | private Node top = null; 12 | 13 | //定义栈结点 14 | private static class Node { 15 | //栈结点数据域 16 | private int data; 17 | //栈结点指针域 18 | private Node next; 19 | //构造函数 20 | public Node(int data, Node next) { 21 | this.data = data; 22 | this.next = next; 23 | } 24 | //get 获取数据域方法 25 | public int getData() { 26 | return data; 27 | } 28 | } 29 | 30 | /** 31 | * 功能:入栈 32 | * @param value:要入栈的数据元素 33 | */ 34 | public void push(int value) { 35 | //创建一个栈结点 36 | Node newNode = new Node(value, null); 37 | // 判断栈是否为空 38 | if (top == null) { 39 | //如果栈为空,就将入栈的值作为栈的第一个元素 40 | top = newNode; 41 | } else { 42 | //否则插入到top栈结点前(所谓的就是单链表的头插法) 43 | newNode.next = top; 44 | top = newNode; 45 | } 46 | } 47 | 48 | /** 49 | * 功能 : 出栈 50 | * @return: -1 为栈中没有数据 51 | */ 52 | public int pop() { 53 | // 如果栈的最顶层栈结点为null,栈为空 54 | if (top == null) return -1; 55 | 56 | //否则执行出栈操作,现将栈顶结点的数据元素赋值给 Value 57 | int value = top.data; 58 | //将 top 指针向下移动 59 | top = top.next; 60 | //返回出栈的值 61 | return value; 62 | } 63 | 64 | /** 65 | * 功能:输出栈中所有元素 66 | */ 67 | public void printAll() { 68 | //将栈顶指针赋值给p 69 | Node p = top; 70 | //循环遍历栈(遍历单链表) 71 | while (p != null) { 72 | System.out.print(p.data + " "); 73 | //指向下一个结点 74 | p = p.next; 75 | } 76 | System.out.println(); 77 | } 78 | } 79 | 80 | ``` 81 | 82 | -------------------------------------------------------------------------------- /Array/JAVA/DynamicDilatationArray.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | import javax.xml.crypto.Data; 4 | 5 | /** 6 | * 功能:实现一个支持动态扩容的数组 7 | * 方法一:System.arraycopy() 8 | * 最好时间复杂度为O(1),最坏时间复杂度为 O(n),平均时间复杂度为 O(1). 9 | * 10 | * 方法二:ArrayList 实现自动扩容 11 | * @author 小鹿 12 | * 13 | */ 14 | 15 | 16 | public class DynamicDilatationArray { 17 | private int count; 18 | private int[] array; 19 | private int num; 20 | private int n; 21 | 22 | //初始化 23 | public DynamicDilatationArray(int number) { 24 | count = 0; 25 | array = new int[number]; 26 | num = number; 27 | n = 1; 28 | } 29 | 30 | 31 | public static void main(String[] args) { 32 | DynamicDilatationArray data = new DynamicDilatationArray(5); 33 | data.insert(1); 34 | data.insert(2); 35 | data.insert(3); 36 | data.insert(4); 37 | data.insert(5); 38 | data.insert(6); 39 | data.insert(7); 40 | data.insert(8); 41 | data.insert(9); 42 | data.insert(10); 43 | data.insert(11); 44 | data.insert(12); 45 | data.print(); 46 | } 47 | 48 | //插入数据 49 | public void insert(int value) { 50 | if(count>=num*n) { 51 | //动态扩容 52 | int[] newArray = new int[array.length*2]; 53 | //数组数据搬移 54 | for(int j =0;j{ 19 | // 判断 k 的范围 20 | if(k < 1 || k > arr.length){ 21 | return false; 22 | } 23 | // 终止条件 24 | if(start > end) return; 25 | // 区分点 26 | let pivot = end; 27 | // 求中间下标 28 | let middleIndex = partition(arr,start,end,pivot); 29 | console.log(arr) 30 | if(middleIndex + 1 == k){ 31 | console.log("第"+k+"大数据为:"+arr[middleIndex]); 32 | return; 33 | }else if(middleIndex + 1 < k){ 34 | largestKelement(arr,middleIndex+1,end,k); 35 | }else{ 36 | largestKelement(arr,start,middleIndex-1,k); 37 | } 38 | } 39 | // 区分点 40 | const partition = (arr,start,end,pivot)=>{ 41 | // 存储中间点 42 | let pivotVal = arr[pivot]; 43 | // 交换点 44 | let startIndex = start; 45 | // 调整数据 46 | for(let i = startIndex;i < end;++i){ 47 | if(arr[i] < pivotVal){ 48 | swap(arr,i,startIndex); 49 | startIndex++; 50 | } 51 | } 52 | swap(arr,startIndex,pivot); 53 | return startIndex; 54 | } 55 | // 交换 56 | const swap = (arr,x,y)=>{ 57 | if (x === y) return; 58 | let temp = arr[x]; 59 | arr[x] = arr[y]; 60 | arr[y] = temp; 61 | } 62 | 63 | // 测试 64 | const testArr = [] 65 | let i = 0 66 | while(i < 5){ 67 | testArr.push(Math.floor(Math.random()*10)); 68 | i++; 69 | } 70 | console.log("Quick:"+testArr) 71 | largestKelement(testArr,0,testArr.length - 1,3); 72 | ``` 73 | 74 | -------------------------------------------------------------------------------- /Sorts/JavaScript/QuickSort.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 2019/3/15 4 | * 公众号: 「一个不甘平凡的码农」 5 | * 功能:快速排序 6 | * @author 小鹿 7 | */ 8 | 9 | /** 10 | * 时间:2019/3/15 11 | * 功能:递归 12 | * 1)终止条件 13 | * 2)选择数组最后一个数据为区分点 14 | * 3) 根据区分点区分 partition 返回区分点的下标索引 15 | * 4)左右部分数据进行递归 16 | * @param arr 要分治的数组 17 | * @param start 数组起始位置 18 | * @param end 数组终止位置 19 | */ 20 | const quickSort = (arr,start,end) => { 21 | // 终止条件 22 | if(start >= end) return; 23 | 24 | //区分点每次选择最后元素 25 | let pivot = end; 26 | 27 | //获取区分点 28 | let partitionIndex = partition(arr,pivot,start,end) 29 | 30 | //区分点两端开始递归 31 | quickSort(arr,start,partitionIndex - 1) 32 | quickSort(arr,partitionIndex + 1,end) 33 | } 34 | 35 | /** 36 | * 时间:2019/3/15 37 | * 功能:获取分区点 pivot 38 | * @param arr 要分治的数组 39 | * @param pivot 区分点 40 | * @param start 数组起始位置 41 | * @param end 数组终止位置 42 | */ 43 | const partition = (arr,pivot,start,end) => { 44 | // 存储区分点元素 45 | const pivotVal = arr[pivot]; 46 | // 大小值交换指针 47 | let startIndex = start; 48 | // 遍历数据根据 pivot 进行划分 49 | for(let i = start; i < end; i++){ 50 | if(arr[i] < pivotVal){ 51 | swap(arr,i,startIndex); 52 | startIndex ++; 53 | } 54 | } 55 | //将 pivot 点元素放入区分点位置 56 | swap(arr,startIndex,pivot); 57 | //返回已排好的区分点 58 | return startIndex; 59 | } 60 | 61 | //交换 62 | const swap = (arr,i,j) => { 63 | const temp = arr[i] 64 | arr[i] = arr[j] 65 | arr[j] = temp 66 | } 67 | 68 | //随机数据进行排序 69 | const testArr = [] 70 | let i = 0 71 | while(i < 100){ 72 | testArr.push(Math.floor(Math.random()*1000)); 73 | i++; 74 | } 75 | 76 | //快速排序测试 77 | quickSort(testArr,0,testArr.length - 1); 78 | console.log("Quick:"+testArr) 79 | 80 | ``` 81 | 82 | -------------------------------------------------------------------------------- /Map/Java/BearthFirstSearch.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * @author 小鹿 5 | * 功能:广度优先遍历 6 | * 7 | */ 8 | 9 | public class BreadthFirstTraversal { 10 | private int v; 11 | private LinkedList adj[]; 12 | 13 | public BreadthFirstTraversal(int v) { 14 | this.v = v; 15 | adj = new LinkedList[v]; 16 | for (int i=0; i(); 18 | } 19 | } 20 | 21 | //无向图一条边存两次 22 | public void addEdge(int s, int t) { 23 | adj[s].add(t); 24 | adj[t].add(s); 25 | } 26 | 27 | // 广度优先遍历两顶点最短路径 28 | public void bfs(int s, int t) { 29 | if (s == t) return; 30 | // 记录历史结点 31 | boolean[] visited = new boolean[v]; 32 | visited[s]=true; 33 | 34 | // 记录每层结点 35 | Queue queue = new LinkedList<>(); 36 | queue.add(s); 37 | 38 | // 记录搜索路径(初始化搜索路径) 39 | int[] prev = new int[v]; 40 | for (int i = 0; i < v; ++i) { 41 | prev[i] = -1; 42 | } 43 | // 44 | while (queue.size() != 0) { 45 | //1、层访问队列出队 46 | int w = queue.poll(); 47 | 48 | //2、循环遍历 w 相邻的顶点 49 | for (int i = 0; i < adj[w].size(); ++i) { 50 | //3、逐个取出相邻的结点 51 | int q = adj[w].get(i); 52 | //4、判断是否遍历过 53 | if (!visited[q]) { 54 | //5、将存储路径 55 | prev[q] = w; 56 | //6、判断当前的顶点是否为终点 57 | if (q == t) { 58 | print(prev, s, t); 59 | return; 60 | } 61 | //7、设置相邻的结点已经给访问过 62 | visited[q] = true; 63 | //8、加入到层级队列中 64 | queue.add(q); 65 | } 66 | } 67 | } 68 | } 69 | 70 | // 递归打印 s->t 的路径 71 | private void print(int[] prev, int s, int t) { 72 | if (prev[t] != -1 && t != s) { 73 | print(prev, s, prev[t]); 74 | } 75 | System.out.print(t + " "); 76 | } 77 | } 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /Map/Java/DepthFirstSearch.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * @author 小鹿 5 | * 功能:深度优先遍历 6 | * 7 | */ 8 | 9 | public class DepthFirstSearch { 10 | 11 | boolean found = false; 12 | private int v; 13 | private LinkedList adj[]; 14 | 15 | public DepthFirstSearch(int v) { 16 | this.v = v; 17 | adj = new LinkedList[v]; 18 | for (int i=0; i(); 20 | } 21 | } 22 | 23 | //无向图一条边存两次 24 | public void addEdge(int s, int t) { 25 | adj[s].add(t); 26 | adj[t].add(s); 27 | } 28 | 29 | public void dfs(int s, int t) { 30 | // 停止递归 31 | found = false; 32 | 33 | boolean[] visited = new boolean[v]; 34 | int[] prev = new int[v]; 35 | // 初始化路径 36 | for (int i = 0; i < v; ++i) { 37 | prev[i] = -1; 38 | } 39 | 40 | recurDfs(s, t, visited, prev); 41 | print(prev, s, t); 42 | } 43 | /** 44 | * 45 | * @param w : 起始点 46 | * @param t : 终 点 47 | * @param visited:记录已经访问过的点 48 | * @param prev:记录起始点到终点的路径 49 | */ 50 | private void recurDfs(int w, int t, boolean[] visited, int[] prev) { 51 | // 递归终止条件(找到终点,不再递归) 52 | if (found == true) return; 53 | // 记录起始位置 54 | visited[w] = true; 55 | // 判断是否找到终点 56 | if (w == t) { 57 | found = true; 58 | return; 59 | } 60 | for (int i = 0; i < adj[w].size(); ++i) { 61 | // 取出当顶点相连接的顶点 62 | int q = adj[w].get(i); 63 | // 判断是否已经访问过 64 | if(!visited[q]) { 65 | // 存储路径 66 | prev[q] = w; 67 | // 进行递归 68 | recurDfs(q, t, visited, prev); 69 | } 70 | } 71 | } 72 | 73 | // 递归打印 s->t 的路径 74 | private void print(int[] prev, int s, int t) { 75 | if (prev[t] != -1 && t != s) { 76 | print(prev, s, prev[t]); 77 | } 78 | System.out.print(t + " "); 79 | } 80 | 81 | } 82 | 83 | ``` 84 | 85 | -------------------------------------------------------------------------------- /动态规划/满减活动源码/满减活动源码.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.xiaolu.combat; 3 | 4 | /** 5 | * 公众号:一个不甘平凡的码农 6 | * 【动态规划】 7 | * 功能:实现淘宝 “满减凑单” 8 | * @author 小鹿 9 | * 10 | */ 11 | public class TBShopping { 12 | 13 | static String[] Snacks = {"百草鸭脖","烧烤味牛肉干","科尔牛板筋","三只松鼠核桃","兰花豆","芒果干","乐事薯片","妙芙欧式蛋糕","瑞士卷","白葡萄干","三只松鼠猪肉干"}; 14 | static String[] p = {"26.9元","24.9元","49.9元","28.9元","14.9元","16.9元","19.9元","19.0元","14.9元","14.9元","26.9元"}; 15 | public static void main(String[] args) { 16 | int[] prices = {269,249,499,289,149,169,199,190,149,149,269}; 17 | ShoppingSnacks(prices,11,1990); 18 | } 19 | 20 | /** 21 | * 22 | * @param prices 各个商品的价格 23 | * @param n 商品的个数 24 | * @param w 满减条件(满 199-99元) 25 | */ 26 | public static void ShoppingSnacks(int[] prices,int n,int w){ 27 | //将商品的价格扩展到三倍 28 | boolean[][] tree = new boolean[n][3*w+1]; 29 | tree[0][0] = true; 30 | tree[0][prices[0]] = true; 31 | 32 | //动态规划 33 | for (int i = 1; i < n; i++) { 34 | 35 | // 不购买当前商品 36 | for(int j = 0;j <=3*w; j++) { 37 | //寻找上一个商品决策状态 38 | if(tree[i-1][j] == true) { 39 | tree[i][j] = true; 40 | } 41 | } 42 | 43 | // 购买当前商品 44 | for(int j = 0;j <=3*w-prices[i]; j++) { 45 | //寻找上一个商品决策状态 46 | if(tree[i-1][j] == true) { 47 | tree[i][j+prices[i]] = true; 48 | } 49 | } 50 | } 51 | 52 | //找出需要凑单的商品 53 | int j; 54 | for(j = w;j < 3*w+1; j++) { 55 | //在最后一个商品寻找满足最接近 200 的条件状态 56 | if(tree[n-1][j]==true) { 57 | System.out.println("满减的最大条件为"+(float)j/10+"元"); 58 | break; 59 | } 60 | } 61 | 62 | //没有可选择零食 63 | if(j == -1) { 64 | return; 65 | } 66 | 67 | // 倒推遍历满足条件的商品 68 | for(int i = n-1; i>=1; i--) { 69 | //当前账单的总金额大必须于当前商品金额 70 | //且上一个商品的决策状态为 true 71 | if(j - prices[i]>=0 && tree[i-1][j-prices[i]] == true) { 72 | //已购买该商品 73 | System.out.println(Snacks[i]+p[i]); 74 | j = j - prices[i]; 75 | }else { 76 | //没有购买该商品 77 | 78 | } 79 | } 80 | if(j != 0) { 81 | System.out.print(Snacks[1]+p[0]); 82 | } 83 | } 84 | } 85 | 86 | ``` 87 | 88 | -------------------------------------------------------------------------------- /Trie/JAVA/Trie 字典树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.xiaolu.Trie; 3 | 4 | /** 5 | * 功能:字典树(Trie 树) 6 | * @author 小鹿 7 | * 8 | */ 9 | public class Trie { 10 | 11 | //Trie 树的结点 12 | public class TrieNode { 13 | 14 | //数据域: 存放的字符数据 15 | public char data; 16 | // 结点中存放的数组 17 | public TrieNode[] children = new TrieNode[26]; 18 | //记录字符是否已经存放完毕 19 | public boolean isEndingChar = false; 20 | //构造函数(初始化) 21 | public TrieNode(char data) { 22 | this.data = data; 23 | } 24 | } 25 | 26 | // 存储无意义字符(根节点为'/') 27 | private TrieNode root = new TrieNode('/'); 28 | 29 | /** 30 | * 往 Trie 树中插入一个字符串 31 | * @param text 要插入的字符串 32 | */ 33 | public void insert(char[] text) { 34 | // p 结点存储为空 35 | TrieNode p = root; 36 | 37 | //循环遍历字符串中的每个字符 38 | for (int i = 0; i < text.length; ++i) { 39 | //计算每个字符所在数组的下标 40 | int index = text[i] - 'a'; 41 | //判断该下标的数据是否已存字符 42 | if (p.children[index] == null) { 43 | //如果没有存放,我们就将该字符存入结点 44 | TrieNode newNode = new TrieNode(text[i]); 45 | //存放该结点 46 | p.children[index] = newNode; 47 | } 48 | //将指针移动到该数组中的 TrieNode 结点 49 | p = p.children[index]; 50 | } 51 | //所有字符存放完毕之后,将值置为 true 52 | p.isEndingChar = true; 53 | } 54 | 55 | /** 56 | * 在 Trie 树中查找一个字符串 57 | * @param pattern 要查找的字符串 58 | * @return 59 | */ 60 | public boolean find(char[] pattern) { 61 | //定义根节点'/' 62 | TrieNode p = root; 63 | //遍历要查找的字符 64 | for (int i = 0; i < pattern.length; ++i) { 65 | //计算字符串中的第一个字符在数组中的下标 66 | int index = pattern[i] - 'a'; 67 | //判断是否存在该字符 68 | if (p.children[index] == null) { 69 | // 不存在 pattern 70 | return false; 71 | }else { 72 | //如果存在,将指针移动到该字符存储的数值中 73 | p = p.children[index]; 74 | } 75 | } 76 | //判断匹配的该字符是否已经完全匹配 77 | if (p.isEndingChar == false) { 78 | return false; // 不能完全匹配,只是前缀 79 | } else { 80 | return true; // 找到 pattern 81 | } 82 | } 83 | } 84 | 85 | 86 | ``` 87 | 88 | -------------------------------------------------------------------------------- /Link_List/javascript/CircularLinkedList.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | 3 | /** 4 | * 时间:2019/3/29 5 | * 公众号:「一个不甘平凡的码农」 6 | * 循环链表的插入、查找、删除 7 | * 功能: 8 | * 1)插入数据 9 | * 2)查找数据 10 | * 3)删除数据 11 | * @author 小鹿 12 | * 13 | */ 14 | //定义循环链表结点 15 | class Node{ 16 | constructor(data){ 17 | this.data = data; 18 | this.next = null; 19 | } 20 | } 21 | 22 | //构造具有一个的循环链表 23 | class LList{ 24 | constructor(){ 25 | this.head = new Node("head"); 26 | this.head.next = this.head; 27 | } 28 | 29 | //按值查找结点 30 | findByValue = (value)=>{ 31 | //将头指针赋值 p 指针 32 | let p = this.head; 33 | //循环遍历查找该值的节点 34 | while(p.data !== value){ 35 | p = p.next; 36 | //判断循环链表是否只有一个节点 37 | if(p === this.head) return false; 38 | } 39 | return p; 40 | } 41 | 42 | //按值插入结点 43 | insertByValue = (value,data)=>{ 44 | //构造新结点 45 | let newNode = new Node(value); 46 | //先按值查找结点 47 | let vp = this.findByValue(data); 48 | //查找到该值进行插入 49 | newNode.next = vp.next; 50 | vp.next = newNode; 51 | } 52 | 53 | //按值删除结点 54 | deleteByValue = (value)=>{ 55 | let p = this.head; 56 | let pp = null; 57 | 58 | //判断循环链表是否只有一个节点 59 | if(p.next === this.head) return false; 60 | //按值查找该结点且记录该结点的前驱结点 61 | while(p.data !== value){ 62 | pp = p; 63 | p = p.next; 64 | } 65 | //查找到该结点进行删除 66 | pp.next = p.next; 67 | } 68 | 69 | //遍历输出所有节点 70 | display = ()=>{ 71 | let p = this.head.next; 72 | while(p !== this.head){ 73 | console.log(p.data); 74 | p = p.next; 75 | } 76 | } 77 | } 78 | 79 | //测试 80 | let clist = new LList(); 81 | console.log('-----------------------插入数据----------------') 82 | clist.insertByValue(1,"head") 83 | clist.insertByValue(2,"head") 84 | clist.insertByValue(3,"head") 85 | clist.display(); 86 | console.log('-----------------------查询数据----------------') 87 | console.log(clist.findByValue(1).data) 88 | console.log('-----------------------删除数据----------------') 89 | clist.deleteByValue(3) 90 | clist.display(); 91 | ``` 92 | 93 | -------------------------------------------------------------------------------- /Trie/JavaScript/Trie.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 时间:2019/3/30 4 | * 公众号:「一个不甘平凡的码农」 5 | * 字典树 6 | * 功能: 7 | * 1)插入数据 8 | * 2)查找数据 9 | * @author 小鹿 10 | * 11 | */ 12 | //定义树节点(数组充当节点) 13 | class TrieNode{ 14 | constructor(data){ 15 | //存储字符 16 | this.data = data; 17 | //存储下一结点的指针 18 | this.children = new Array(26); 19 | } 20 | } 21 | //字典树 22 | class TrieTree{ 23 | constructor(data){ 24 | //根节点不存储数据 25 | this.root = new TrieNode('/'); 26 | } 27 | 28 | //插入数据 29 | //步骤: 30 | //1、遍历字符串 31 | //2、计算字符的下标索引 32 | //3、判断该下标是否存在数据进行插入 33 | //4、遍历下一结点 34 | insertByValue = (word)=>{ 35 | let node = this.root; 36 | //for 循环遍历插入数据 37 | for(let char of word){ 38 | //获取下标索引 39 | let index = char.charCodeAt() - 'a'.charCodeAt(); 40 | //判断该下标的数据是否已存字符 41 | if(node.children[index] == null){ 42 | //如果没有存放,就将该字符存入结点 43 | node.children[index] = new TrieNode(char); 44 | } 45 | //指向下一结点 46 | node = node.children[index]; 47 | } 48 | return true; 49 | } 50 | 51 | //查找数据 52 | //步骤: 53 | //1、遍历字符串 54 | //2、计算字符串的下标索引 55 | //3、判断该索引的数据为 null 56 | //4、继续遍历 57 | findByValue = (word)=>{ 58 | let node = this.root; 59 | //遍历单词开始查询 60 | for(let char of word){ 61 | let index = char.charCodeAt() - 'a'.charCodeAt(); 62 | //判断是否存在该字符 63 | if(node.children[index] == null){ 64 | return false; 65 | }else{ 66 | node = node.children[index]; 67 | } 68 | } 69 | return true; 70 | } 71 | } 72 | 73 | //测试 74 | const tree = new TrieTree(); 75 | let strs = ['how','hi','her','hello','so','see']; 76 | console.log('-----------------------插入数据-----------------------') 77 | for(let str of strs){ 78 | tree.insertByValue(str); 79 | } 80 | console.log('-----------------------查找存在数据-----------------------') 81 | for(let str of strs){ 82 | console.log(tree.findByValue(str)); 83 | } 84 | console.log('-----------------------查找不存在数据-----------------------') 85 | console.log(tree.findByValue('world')); 86 | 87 | ``` 88 | 89 | -------------------------------------------------------------------------------- /Queue/JAVA/ArrayQueue.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | import java.util.concurrent.ForkJoinPool.ManagedBlocker; 5 | 6 | /** 7 | * 公众号:「一个不甘平凡的码农」 8 | * 时间:2019/3/2 9 | * 功能:队列 10 | * 1)入队 11 | * 2)出队 12 | * @author 小鹿 13 | * 14 | */ 15 | 16 | public class Queue { 17 | 18 | //声明一个 int 队列 19 | private int[] items; 20 | private int n; 21 | 22 | //声明头、尾指针 23 | private int head = 0; 24 | private int tail = 0; 25 | 26 | //初始化变量 27 | public Queue(int capacity) { 28 | items = new int[capacity]; 29 | n = capacity; 30 | } 31 | 32 | public static void main(String[] args) { 33 | Queue queue = new Queue(5); 34 | //入队 35 | queue.enqueue2(1); 36 | queue.enqueue2(2); 37 | queue.enqueue2(3); 38 | queue.enqueue2(4); 39 | queue.enqueue2(5); 40 | //出队 41 | queue.dequeue(); 42 | queue.dequeue(); 43 | //搬移数据入队 44 | queue.enqueue2(6); 45 | queue.enqueue2(7); 46 | queue.print(); 47 | } 48 | 49 | /** 50 | * 时间:2019/3/2 51 | * 功能:入队 52 | * 存在的不足:浪费内存空间 53 | * @param data 元素 54 | */ 55 | public Boolean enqueue(int data) { 56 | //队满 57 | if(tail == n) return false; 58 | //入队 59 | items[tail] = data; 60 | //指针向后移动 61 | tail++; 62 | return true; 63 | } 64 | 65 | /** 66 | * 功能:优化后入队 67 | * 边界分析: 68 | * 1)判断队满 69 | * 2)判断数据是否可向前移动 70 | * @param data 元素 71 | */ 72 | public Boolean enqueue2(int data) { 73 | //队满 74 | if(tail == n) { 75 | if(head == 0) { 76 | return false; 77 | }else { 78 | //数据搬移(技巧) 79 | for (int i = head; i < tail; i++) { 80 | items[i - head] = items[i]; 81 | } 82 | tail = tail - head; 83 | head = 0; 84 | } 85 | } 86 | //插入数据 87 | items[tail] = data; 88 | tail++; 89 | return true; 90 | } 91 | 92 | /** 93 | * 时间:2019/3/2 94 | * 功能:出队 95 | * 存在的不足:浪费内存空间 96 | */ 97 | public int dequeue() { 98 | //队空 99 | if(head == tail) return -1; 100 | //出队 101 | int temp = items[head]; 102 | head++; 103 | return temp; 104 | } 105 | 106 | /** 107 | * 功能:打印队列 108 | */ 109 | public void print() { 110 | for (int i = head; i < tail; i++) { 111 | System.out.print(items[i]+" "); 112 | } 113 | } 114 | } 115 | ``` 116 | 117 | -------------------------------------------------------------------------------- /Heap/JavaScript/HeapSort.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 公众号:一个不甘平凡的码农 4 | * 2019/3/20 5 | * 堆排序 6 | * 算法思路: 7 | * 1)建堆 8 | * 2)排序 9 | * 3)测试 10 | * Author: 小鹿 11 | */ 12 | class HeapSort{ 13 | //构造函数(传入数组) 14 | constructor(originArray){ 15 | this.originArray = originArray 16 | console.log(this.originArray) 17 | } 18 | 19 | 20 | /** 21 | * 建堆(从上到下) 22 | * 返回数组 23 | */ 24 | buildHeap = () =>{ 25 | const arr = this.originArray 26 | const startIndex = Math.floor(arr.length)/2 27 | //对非叶子元素进行堆化 28 | for(let i = startIndex; i >= 1; i--){ 29 | this.heapify(arr,arr.length,i) 30 | } 31 | return arr; 32 | } 33 | 34 | //堆化 35 | //arr:堆化的数组 36 | //len数组的长度 37 | //i 要堆化的元素 38 | 39 | /** 40 | * 堆化 41 | * arr: 堆化的数组 42 | * len:数组的长度 43 | * i :要堆化的元素 44 | */ 45 | heapify = (arr,len,i) =>{ 46 | while(true){ 47 | //存储要堆化元素的下标 48 | let maxPos = i; 49 | //先比较与左子节点的大小 50 | if(2*i <= len && arr[i] < arr[2*i] ) maxPos = 2*i; 51 | //比较右子节点 52 | if(2*i+1 <= len && arr[maxPos] < arr[2*i+1]) maxPos = 2*i + 1; 53 | 54 | //如果根节点没有子节点或比两个子节点小,直接返回 55 | if(maxPos === i){ 56 | break; 57 | }else{ 58 | this.swap(arr,maxPos,i); 59 | //继续往下堆化 60 | i = maxPos; 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * 堆排序 67 | */ 68 | sort = () =>{ 69 | //建堆 70 | const arr = this.buildHeap(); 71 | let len = arr.length - 1; 72 | //排序(条件:结点大于两个) 73 | while(len > 1){ 74 | this.swap(arr,1,len); 75 | len--; 76 | this.heapify(arr,len,1); 77 | } 78 | console.log(arr) 79 | } 80 | 81 | // 两个数组内元素交换 82 | swap = (arr,x,y) =>{ 83 | let temp = arr[x]; 84 | arr[x] = arr[y]; 85 | arr[y] = temp; 86 | } 87 | } 88 | 89 | const arr = [null] 90 | let i = 0 91 | while (i <= 10) { 92 | const num = Math.floor(Math.random() * 100) 93 | arr.push(num) 94 | i++ 95 | } 96 | const test = new HeapSort(arr); 97 | test.sort(); 98 | ``` 99 | 100 | -------------------------------------------------------------------------------- /Heap/JAVA/HeapSort.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.xiaolu.Heap; 3 | 4 | /** 5 | * 公众号:「一个不甘平凡的码农」 6 | * 时间:2019/2/28 7 | * 功能:堆排序 8 | * @author 小鹿 9 | * 10 | */ 11 | public class HeapSort { 12 | 13 | private static int[] a; 14 | private static int n; 15 | 16 | public HeapSort(int capitity) { 17 | a = new int[] {1,4,6,3,7,2}; 18 | n = capitity; 19 | } 20 | 21 | public static void main(String[] args) { 22 | HeapSort heapSort = new HeapSort(6); 23 | heapSort.sort(a, n); 24 | heapSort.print(); 25 | } 26 | 27 | /** 28 | * 时间:2019/2/28 29 | * 功能:建堆 30 | * 1)取数组中间从后往前 31 | * @param a 数组 32 | * @param n 数组的大小 33 | */ 34 | public static void buildHeap(int[] a,int n) { 35 | for (int i = n/2; i >= 1; i--) { 36 | //倒数第二节点开始所有节点从上到下堆化 37 | heapify(a,n,i); 38 | } 39 | } 40 | /** 41 | * 功能:从上自下堆化 42 | * @param a 堆化的数组 43 | * @param count 当前堆中的数据 44 | * @param i 非叶子节点数据 45 | */ 46 | private static void heapify(int[] a,int n,int i) { 47 | while(true) { 48 | int maxPos = i; 49 | //选择子节点中最大的数据作交换 50 | if(i*2 < n && a[i] < a[i*2]) maxPos = i*2; 51 | if(i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1; 52 | 53 | //如果堆顶只剩一个元素,break 54 | if(maxPos == i) break; 55 | 56 | //进行交换 57 | swap(a, i, maxPos); 58 | //继续往下堆化 59 | i = maxPos; 60 | } 61 | } 62 | 63 | /** 64 | * 时间:2019/3/3 65 | * 功能:堆排序 66 | * 思路:大顶堆的堆顶数据最大,每次将堆顶的元素放到数据的尾部(也就是堆中删除数据的过程),先进行交换,然后进行从上到下堆化。 67 | * 分析: 68 | * 1)建堆(非叶子节点从上到下堆化) 69 | * 2)排序(堆顶元素的删除) 70 | * 边界条件: 71 | * 1)堆内剩余数据是否大于1(交换) 72 | * @param a 数组 73 | * @param n 数据的个数 74 | */ 75 | public static void sort(int[] a, int n) { 76 | //1、建堆 77 | buildHeap(a, n); 78 | 79 | //2、排序 80 | int k = n-1; 81 | while (k > 1) { 82 | //交换堆顶元素和最后一个元素 83 | swap(a, 1, k); 84 | //堆元素减一 85 | --k; 86 | //从上向下进行堆化 87 | heapify(a, k, 1); 88 | } 89 | } 90 | 91 | /** 92 | * 功能:进行数据交换 93 | * @param a 数据 1 94 | * @param b 数据 2 95 | */ 96 | private static void swap(int[] a, int x,int y) { 97 | int temp; 98 | temp = a[x]; 99 | a[x] = a[y]; 100 | a[y] = temp; 101 | } 102 | 103 | /** 104 | * 时间:2019/3/3 105 | * 功能:打印堆内数据 106 | */ 107 | public void print() { 108 | for (int i = 0; i < a.length; i++) { 109 | System.out.print(a[i]+" "); 110 | } 111 | } 112 | } 113 | 114 | ``` 115 | 116 | -------------------------------------------------------------------------------- /Array/JAVA/FixedOrderArray.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | /** 6 | * 实现一个大小固定的有序数组,支持动态增删改操作 7 | * @author 小鹿 8 | * 9 | */ 10 | public class FixedOrderArray { 11 | private int[] data; 12 | private int n; 13 | private int count; 14 | 15 | public FixedOrderArray(int n) { 16 | this.n = n; 17 | count = 0; 18 | data = new int[n]; 19 | } 20 | 21 | public static void main(String[] args) { 22 | FixedOrderArray array02 = new FixedOrderArray(5); 23 | //插入 24 | array02.insert(1);//0 25 | array02.insert(3);//1 26 | array02.insert(2);//2 27 | array02.insert(1);//3 28 | array02.insert(0);//4 29 | //删除 30 | array02.delete(0); 31 | array02.print(); 32 | //查找数据 33 | System.out.println(); 34 | System.out.print("查找该数据的下标为:"+array02.find(2)); 35 | } 36 | 37 | /** 38 | * 功能:插入 39 | * @param value 插入的元素 40 | * @return 返回 Boolean 值 41 | */ 42 | public Boolean insert(int value) { 43 | //判断数组是否为空 44 | if(data == null && data.length==0) { 45 | return false; 46 | }else { 47 | //第一个数据直接插入数组 48 | if(count == 0) { 49 | data[count] = value; 50 | count++; 51 | return true; 52 | } 53 | //优化:先判断是否大于最后一个数据 54 | if(value>=data[count-1]) { 55 | data[count] = value; 56 | count++; 57 | return true; 58 | } 59 | //其他数据比较搬移 60 | for (int i = 0; i < data.length; i++) { 61 | if(value >= data[i]) { 62 | for (int j = count-1; j >= i+1; j--) { 63 | data[j+1] = data[j]; 64 | } 65 | data[i+1] = value; 66 | count++; 67 | return true; 68 | }else { 69 | //插入数组第一个位置 70 | for (int j = count-1; j >=0; j--) { 71 | data[j+1] = data[j]; 72 | } 73 | data[0] = value; 74 | count++; 75 | return true; 76 | } 77 | } 78 | } 79 | return false; 80 | } 81 | 82 | 83 | /** 84 | * 功能:删除下标指定数据 85 | * @param index 指定下标 86 | * @return 返回删除的数据 87 | */ 88 | public int delete(int index) { 89 | //判断删除元素的下标是否合理 90 | if(index < 0 || index >= data.length) { 91 | return -1; 92 | }else { 93 | for (int i = index; i < count-1; i++) { 94 | data[i] = data[i+1]; 95 | } 96 | } 97 | count--; 98 | return 0; 99 | } 100 | 101 | /** 102 | * 功能: 查找 103 | * @param value 要查找的元素 104 | * @return 返回该下标 105 | */ 106 | public int find(int value) { 107 | 108 | for (int i = 0; i <= count-1; i++) { 109 | if(data[i]==value) { 110 | return i; 111 | } 112 | } 113 | return -1; 114 | } 115 | 116 | //打印数组内容 117 | public void print() { 118 | for(int i=0;i{ 22 | if(a.length < 1) return; 23 | for(let i = 0;i < a.length;i++){ 24 | for(let j = 0;j < a.length-1-i;j++){ 25 | if(a[j]>=a[j+1]){ 26 | let temp = a[j]; 27 | a[j] = a[j+1]; 28 | a[j+1] = temp; 29 | flag = true; 30 | } 31 | } 32 | if(!flag){ 33 | break; 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * 时间:2019/3/14 40 | * 功能:插入排序 41 | * 边界条件: 42 | * 1)判断数组是否有数据 43 | * 算法思路: 44 | * 1)外循环指针指向未排序区间的第一个数据 a[1],内循环指针指向排序区间的第一个数数据 a[0] 45 | * 2)将未排序区间的第一个数据存储到临时变量(value),与排序区间逐个比较大小。 46 | * 3)如果小于排序区间的数据,已排序区间数据向后移动一位a[j+1] = a[j];否则直接break,插入到数组合适位置。 47 | * @param a:数组 48 | * @param n:数组的大小 49 | */ 50 | //插入排序 51 | const insertsort = (a) => { 52 | if(a.length <= 1) return; 53 | for(let i = 1;i < a.length;i++){ 54 | let value = a[i]; 55 | for (var j = i-1; j >= 0; --j) { 56 | if (a[j] > value) { 57 | a[j+1] = a[j]; // 数据移动 58 | } else { 59 | break; 60 | } 61 | } 62 | a[j+1] = value; // 插入数据 63 | } 64 | } 65 | 66 | 67 | /** 68 | * 时间:2019/3/15 69 | * 功能:选择排序 70 | * 边界条件: 71 | * 1)判断数组是否有数据 72 | * 算法思路: 73 | * 1)外循环 0 -> n-1 设定最小数据 74 | * 2)内循环 i+1->n 范围数据作比较选出最小数据 75 | * 3)如果当前最小,则继续循环 76 | * 4)做交换 77 | * @param a:数组 78 | * @param n:数组的大小 79 | */ 80 | const selectSort = (a) => { 81 | //如果数组为空,结束排序 82 | if (a.length <= 1) return; 83 | //外循环,i 用来标记未排序区间的第一个元素,将其假设为未排序区间的最小值 84 | for(var i = 0; i < a.length-1; i++){ 85 | var minIndex = i; 86 | for(var j = i+1;j < a.length;j++){ 87 | if(a[j] < a[minIndex]){ 88 | minIndex = j; 89 | } 90 | } 91 | if(minIndex == i){ 92 | continue; 93 | } 94 | var temp = a[i]; 95 | a[i] = a[minIndex]; 96 | a[minIndex] = temp; 97 | } 98 | } 99 | 100 | //测试 101 | //冒泡排序 102 | var a = [2,5,8,1,3,9]; 103 | bubblesort(a); 104 | console.log(a); 105 | //插入排序 106 | var a = [6,9,3,0,1,8] 107 | insertsort(a); 108 | console.log(a); 109 | //选择排序 110 | var a = [12,5,3,16,5]; 111 | selectSort(a); 112 | console.log(a); 113 | ``` 114 | 115 | -------------------------------------------------------------------------------- /Heap/JAVA/Heap.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.xiaolu.Heap; 3 | 4 | /** 5 | * 公众号:「一个不甘平凡的码农」 6 | * 时间:2019/2/28 7 | * 功能:堆的插入与删除 8 | * @author 小鹿 9 | * 10 | */ 11 | public class Heap { 12 | //数组,从 1 开始存储 13 | private static int[] a; 14 | //记录堆可以存储的最大数据 15 | private int n; 16 | //记录已经存储的数据个数 17 | private int count; 18 | //初始化数据 19 | public Heap(int capacity) { 20 | a = new int[capacity + 1]; 21 | n = capacity; 22 | count = 0; 23 | } 24 | 25 | public static void main(String[] args) { 26 | Heap heap = new Heap(5); 27 | //插入数据 28 | heap.insert(1); 29 | heap.insert(5); 30 | heap.insert(3); 31 | heap.insert(6); 32 | heap.print(a); 33 | //移除最大数据 34 | heap.removeMax(); 35 | System.out.println(""); 36 | heap.print(a); 37 | 38 | } 39 | 40 | 41 | /** 42 | * 时间:2019/2/28 43 | * 功能:插入数据 44 | * 分析: 45 | * 1)插入到数组尾部(堆最后的节点) 46 | * 2)自下而上堆化(比较和交换) 47 | * 边界条件: 48 | * 1)判断是否已经堆满 49 | * 2) 50 | * @param data 插入的数据 51 | */ 52 | public void insert(int data) { 53 | //判断堆是否已满 54 | if(count >= n) return; 55 | //计数+1(下标 0 不存储数据) 56 | count++; 57 | //将数据插入最后节点 58 | a[count] = data; 59 | 60 | //自下往上堆化 61 | int i = count; 62 | //判断插入数据的根节点是否大于 0 且插入的数据是否大于根节点数据 63 | while(i/2 > 0 && a[i] > a[i/2]) { 64 | //交换下标为 i 和 i/2 的元素 65 | swap(a,i,i/2); 66 | //继续堆化比较交换 67 | i = i/2; 68 | } 69 | } 70 | 71 | /** 72 | * 时间:2019/2/28 73 | * 功能:删除堆顶数据 74 | * 分析: 75 | * 1)判断堆是否为空 76 | * 2)数组长度 -1 77 | * 3)从下标为 1 的数据从上到下进行堆化 78 | * 4)在左右节点中选出最大元素进行交换 79 | * 边界分析: 80 | * 1)判断堆是否为空 81 | * 2)堆中是否只有一个元素 82 | * @param data 要删除的数据 83 | */ 84 | public void removeMax() { 85 | //如果堆中无数据,则返回 86 | if(count == 0) return; 87 | //删除堆顶的第一个元素,与最后一个元素进行交换 88 | a[1] = a[count]; 89 | //数组长度-1 90 | count--; 91 | //从上到下进行堆化 92 | heapify(a,count,1); 93 | } 94 | /** 95 | * 时间:2019/2/28 96 | * 功能:从上自下堆化 97 | * @param a 堆化的数组 98 | * @param count 当前堆中的数据 99 | * @param i 100 | */ 101 | private void heapify(int[] a,int n,int i) { 102 | while(true) { 103 | int maxPos = i; 104 | //选择子节点中最大的数据作交换 105 | if(i*2 < n && a[i] < a[i*2]) maxPos = i*2; 106 | if(i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1; 107 | 108 | //如果堆顶只剩一个元素,break 109 | if(maxPos == i) break; 110 | 111 | //进行交换 112 | swap(a, i, maxPos); 113 | //继续往下堆化 114 | i = maxPos; 115 | } 116 | } 117 | 118 | /** 119 | * 时间:2019/2/28 120 | * 功能:遍历堆中的所有数据 121 | * @param a 数组 122 | */ 123 | public void print(int[] a) { 124 | for (int i = 0; i < a.length; i++) { 125 | System.out.print(a[i]+ " "); 126 | } 127 | } 128 | 129 | /** 130 | * 时间:2019/2/28 131 | * 功能:进行数据交换 132 | * @param a 数据 1 133 | * @param b 数据 2 134 | */ 135 | private void swap(int[] a, int x,int y) { 136 | int temp; 137 | temp = a[x]; 138 | a[x] = a[y]; 139 | a[y] = temp; 140 | } 141 | } 142 | ``` 143 | 144 | -------------------------------------------------------------------------------- /Sorts/JAVA/sorts.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package easy_test; 3 | 4 | /** 5 | * 功能:冒泡排序、插入排序、选择排序 6 | * @author : 小鹿 7 | * 8 | */ 9 | public class Sorts { 10 | 11 | /** 12 | * 功能:冒泡排序 13 | * @param a:数组 14 | * @param n:数组的大小 15 | */ 16 | public static void bubbleSort(int[] a, int n) { 17 | //如果数组为空,结束排序 18 | if (n <= 1) return; 19 | 20 | //否则冒泡排序(外层循环是 n 个数据需要冒泡 n - 1 次) 21 | for (int i = 0; i <= n - 1; ++i) { 22 | // 提前退出标志位 23 | boolean flag = false; 24 | //内层循环是(每趟循环交换的次数,每趟的交换次数是总数据-1) 25 | for (int j = 0; j < n - 1 - i ; ++j) { 26 | if (a[j] > a[j+1]) { 27 | int tmp = a[j]; 28 | a[j] = a[j+1]; 29 | a[j+1] = tmp; 30 | // 此次冒泡有数据交换 31 | flag = true; 32 | } 33 | } 34 | if (!flag) break; // 没有数据交换,提前退出 35 | } 36 | } 37 | 38 | /** 39 | * 功能:插入排序 40 | * 算法思路:取出未排序区间的数据放到value变量中,与已排序区间的数据倒序一一比较,如果要插入的数据比value大 41 | * 就放到该数据的后方(也就是i的位置,我们用j+1替换(j+1=i)),前边是与排序区间的最后一个数据比较,如果我们value 42 | * 比最后一个比较的数据要小,我们要把被比较的数据向后移动一位(a[j+1] = a[j]),指针 j 向前移动,指向下一个被比较的数据, 43 | * 如果满足条件,则插入到给数据的前方。 44 | * @param a:数组 45 | * @param n:数组的大小 46 | */ 47 | public static void insertionSort(int[] a, int n) { 48 | //如果数组为空,结束排序 49 | if (n <= 1) return; 50 | //否则,进行插入排序 51 | for (int i = 1; i < n; ++i) { 52 | //从下标为1的数据开始与前边元素进行比较,找到合适位置进行插入 53 | int value = a[i]; 54 | //j指针下边比 i 小 1(在 i 指针的) 55 | int j = i - 1; 56 | // 查找要插入的位置并移动数据 57 | for (; j >= 0; --j) { 58 | //要插入数据与已排序的数据进行一一比较 59 | if (a[j] > value) { 60 | //不满足条件就进行数据向后移动,指针J指向下一个被比较数据 61 | a[j+1] = a[j]; 62 | } else { 63 | break; 64 | } 65 | } 66 | //找到合适位置,进行插入 67 | a[j+1] = value; 68 | } 69 | } 70 | 71 | /** 72 | * 功能:选择排序 73 | * 算法思想:在未排序的区间里选择最小的数据元素放到已排好序的区间的末尾。 74 | * @param a:要排序的数组 75 | * @param n:数组的长度 76 | */ 77 | public static void selectionSort(int[] a, int n) { 78 | //如果数组为空,结束排序 79 | if (n <= 1) return; 80 | 81 | // for (int i = 0; i < n; ++i) { 82 | // // 查找最小值 83 | // int minIndex = i; 84 | // int minValue = a[i]; 85 | // for (int j = i; j < n; ++j) { 86 | // if (a[j] < minValue) { 87 | // minValue = a[j]; 88 | 89 | //外循环,i 用来标记未排序区间的第一个元素,将其假设为未排序区间的最小值 90 | for (int i = 0; i < n - 1; ++i) { 91 | // 查找最小值 92 | int minIndex = i; 93 | //在未排序区间进行遍历寻找最小数据 94 | for (int j = i + 1; j < n; ++j) { 95 | //与当前最小数据进行比较 96 | if (a[j] < a[minIndex]) { 97 | //如果比当前最小数据还要小,则将下标给 minIndex 98 | minIndex = j; 99 | } 100 | } 101 | //如果当前的 i 就是最小数据,则继续寻找下一个数据 102 | if (minIndex == i) { 103 | continue; 104 | } 105 | // 将在区间选出的最小数据放到已排好数据的末尾 106 | int tmp = a[i]; 107 | a[i] = a[minIndex]; 108 | a[minIndex] = tmp; 109 | } 110 | } 111 | } 112 | 113 | ``` 114 | 115 | -------------------------------------------------------------------------------- /Link_List/javascript/链表环的检测.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * 2019/3/24 5 | * @author 小鹿 6 | * 功能:链表中环的检测 7 | */ 8 | 9 | //定义结点 10 | class Node{ 11 | constructor(data){ 12 | this.data = data; 13 | this.next = null; 14 | } 15 | } 16 | 17 | //定义链表 18 | class LinkedList{ 19 | constructor(){ 20 | this.head = new Node('head'); 21 | } 22 | 23 | //根据 value 查找结点 24 | findByValue = (value) =>{ 25 | let currentNode = this.head; 26 | while(currentNode !== null && currentNode.data !== value){ 27 | currentNode = currentNode.next; 28 | } 29 | //判断该结点是否找到 30 | console.log(currentNode) 31 | return currentNode === null ? -1 : currentNode; 32 | } 33 | 34 | //根据 index 查找结点 35 | findByIndex = (index) =>{ 36 | let pos = 0; 37 | let currentNode = this.head; 38 | while(currentNode !== null && pos !== index){ 39 | currentNode = currentNode.next; 40 | pos++; 41 | } 42 | //判断是否找到该索引 43 | console.log(currentNode) 44 | return currentNode === null ? -1 : currentNode; 45 | } 46 | 47 | //插入元素(指定元素向后插入) 48 | insert = (value,element) =>{ 49 | //先查找该元素 50 | let currentNode = this.findByValue(element); 51 | //如果没有找到 52 | if(currentNode == -1){ 53 | console.log("未找到插入位置!") 54 | return; 55 | } 56 | let newNode = new Node(value); 57 | newNode.next = currentNode.next; 58 | currentNode.next = newNode; 59 | } 60 | 61 | //根据值删除结点 62 | delete = (value) =>{ 63 | let currentNode = this.head; 64 | let preNode = null; 65 | while(currentNode !== null && currentNode.data !== value){ 66 | preNode = currentNode; 67 | currentNode = currentNode.next; 68 | } 69 | if(currentNode == null) return -1; 70 | preNode.next = currentNode.next; 71 | } 72 | 73 | //遍历所有结点 74 | print = () =>{ 75 | let currentNode = this.head 76 | //如果结点不为空 77 | while(currentNode !== null){ 78 | console.log(currentNode.data) 79 | currentNode = currentNode.next; 80 | } 81 | } 82 | //功能:验证链表为环 83 | //步骤: 84 | // 1、定义两个指针(low、fast) 85 | // 2、fast 指针和下一指向不能为 null 86 | // 3、fast 移动二步,low 前进一步 87 | // 4、判断 fast 和 low 是否相等 88 | checkCicle = () =>{ 89 | let fast = this.head.next; 90 | let low = this.head; 91 | while(fast !== null && fast.next !== null){ 92 | fast = fast.next.next; 93 | low = low.next; 94 | if(low === fast) return true; 95 | } 96 | return false; 97 | } 98 | } 99 | 100 | // 测试 101 | const list = new LinkedList(); 102 | list.insert('1','head'); 103 | list.insert('2','1'); 104 | list.insert('3','2'); 105 | list.insert('4','3'); 106 | list.insert('5','4'); 107 | list.insert('6','5'); 108 | list.print(); 109 | console.log('---------------------检测环----------------------') 110 | console.log(list.checkCicle()) 111 | ``` 112 | 113 | -------------------------------------------------------------------------------- /Link_List/javascript/SinglyLinkedList.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | 3 | /** 4 | * 2019/3/23 5 | * 公众号:「一个不甘平凡的码农」 6 | * @author 小鹿 7 | * 功能:单链表的插入、删除、查找 8 | * 【插入】:插入到指定元素后方 9 | * 1、查找该元素是否存在? 10 | * 2、没有找到返回 -1 11 | * 3、找到进行创建结点并插入链表。 12 | * 13 | * 【查找】:按值查找/按索引查找 14 | * 1、判断当前结点是否等于null,且是否等于给定值? 15 | * 2、判断是否可以找到该值? 16 | * 3、没有找到返回 -1; 17 | * 4、找到该值返回结点; 18 | * 19 | * 【删除】:按值删除 20 | * 1、判断是否找到该值? 21 | * 2、找到记录前结点,进行删除; 22 | * 3、找不到直接返回-1; 23 | */ 24 | //定义结点 25 | class Node{ 26 | constructor(data){ 27 | this.data = data; 28 | this.next = null; 29 | } 30 | } 31 | 32 | //定义链表 33 | class LinkList{ 34 | constructor(){ 35 | //初始化头结点 36 | this.head = new Node('head'); 37 | } 38 | 39 | //根据 value 查找结点 40 | findByValue = (value) =>{ 41 | let currentNode = this.head; 42 | while(currentNode !== null && currentNode.data !== value){ 43 | currentNode = currentNode.next; 44 | } 45 | //判断该结点是否找到 46 | console.log(currentNode) 47 | return currentNode === null ? -1 : currentNode; 48 | } 49 | 50 | //根据 index 查找结点 51 | findByIndex = (index) =>{ 52 | let pos = 0; 53 | let currentNode = this.head; 54 | while(currentNode !== null && pos !== index){ 55 | currentNode = currentNode.next; 56 | pos++; 57 | } 58 | //判断是否找到该索引 59 | console.log(currentNode) 60 | return currentNode === null ? -1 : currentNode; 61 | } 62 | 63 | //插入元素(指定元素向后插入) 64 | insert = (value,element) =>{ 65 | //先查找该元素 66 | let currentNode = this.findByValue(element); 67 | //如果没有找到 68 | if(currentNode == -1){ 69 | console.log("未找到插入位置!") 70 | return; 71 | } 72 | let newNode = new Node(value); 73 | newNode.next = currentNode.next; 74 | currentNode.next = newNode; 75 | } 76 | 77 | //根据值删除结点 78 | delete = (value) =>{ 79 | let currentNode = this.head; 80 | let preNode = null; 81 | while(currentNode !== null && currentNode.data !== value){ 82 | preNode = currentNode; 83 | currentNode = currentNode.next; 84 | } 85 | if(currentNode == null) return -1; 86 | preNode.next = currentNode.next; 87 | } 88 | 89 | //遍历所有结点 90 | print = () =>{ 91 | let currentNode = this.head 92 | //如果结点不为空 93 | while(currentNode !== null){ 94 | console.log(currentNode.data) 95 | currentNode = currentNode.next; 96 | } 97 | } 98 | } 99 | 100 | //测试 101 | const list = new LinkList() 102 | list.insert('xiao','head'); 103 | list.insert('lu','xiao'); 104 | list.insert('ni','head'); 105 | list.insert('hellow','head'); 106 | list.print() 107 | console.log('-------------删除元素------------') 108 | list.delete('ni') 109 | list.delete('xiao') 110 | list.print() 111 | console.log('-------------按值查找------------') 112 | list.findByValue('lu') 113 | console.log('-------------按索引查找------------') 114 | list.print() 115 | list.findByIndex(1) 116 | ``` 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Link_List/javascript/求链表的中间结点.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * 2019/3/24 5 | * @author 小鹿 6 | * 功能:求链表的中间节点 7 | */ 8 | 9 | //定义结点 10 | class Node{ 11 | constructor(data){ 12 | this.data = data; 13 | this.next = null; 14 | } 15 | } 16 | 17 | //定义链表 18 | class LinkedList{ 19 | constructor(){ 20 | this.head = new Node('head'); 21 | } 22 | 23 | //根据 value 查找结点 24 | findByValue = (value) =>{ 25 | let currentNode = this.head; 26 | while(currentNode !== null && currentNode.data !== value){ 27 | currentNode = currentNode.next; 28 | } 29 | //判断该结点是否找到 30 | console.log(currentNode) 31 | return currentNode === null ? -1 : currentNode; 32 | } 33 | 34 | //根据 index 查找结点 35 | findByIndex = (index) =>{ 36 | let pos = 0; 37 | let currentNode = this.head; 38 | while(currentNode !== null && pos !== index){ 39 | currentNode = currentNode.next; 40 | pos++; 41 | } 42 | //判断是否找到该索引 43 | console.log(currentNode) 44 | return currentNode === null ? -1 : currentNode; 45 | } 46 | 47 | //插入元素(指定元素向后插入) 48 | insert = (value,element) =>{ 49 | //先查找该元素 50 | let currentNode = this.findByValue(element); 51 | //如果没有找到 52 | if(currentNode == -1){ 53 | console.log("未找到插入位置!") 54 | return; 55 | } 56 | let newNode = new Node(value); 57 | newNode.next = currentNode.next; 58 | currentNode.next = newNode; 59 | } 60 | 61 | //根据值删除结点 62 | delete = (value) =>{ 63 | let currentNode = this.head; 64 | let preNode = null; 65 | while(currentNode !== null && currentNode.data !== value){ 66 | preNode = currentNode; 67 | currentNode = currentNode.next; 68 | } 69 | if(currentNode == null) return -1; 70 | preNode.next = currentNode.next; 71 | } 72 | 73 | //遍历所有结点 74 | print = () =>{ 75 | let currentNode = this.head 76 | //如果结点不为空 77 | while(currentNode !== null){ 78 | console.log(currentNode.data) 79 | currentNode = currentNode.next; 80 | } 81 | } 82 | 83 | //功能:求中间结点 84 | //步骤: 85 | // 1、声明两个指针(fast、slow) 86 | // 2、判断 fast 的下一结点和下下结点是否为 null? 87 | // 3、如果满足条件,fast 前移动两位, slow 前移动一位 88 | // 返回 slow 就是中间结点 89 | findMidNode = () =>{ 90 | let fast = this.head; 91 | let slow = this.head; 92 | while(fast.next !== null && fast.next.next !== null){ 93 | fast = fast.next.next; 94 | slow = slow.next; 95 | } 96 | console.log(slow) 97 | return slow; 98 | } 99 | } 100 | 101 | // 测试 102 | const list = new LinkedList(); 103 | list.insert('1','head'); 104 | list.insert('2','1'); 105 | list.insert('3','2'); 106 | list.insert('4','3'); 107 | list.insert('5','4'); 108 | list.insert('6','5'); 109 | list.print(); 110 | console.log('--------------------求链表的中间结点---------------------') 111 | console.log(list.findMidNode().data) 112 | ``` 113 | 114 | -------------------------------------------------------------------------------- /Array/JAVA/Array.md: -------------------------------------------------------------------------------- 1 | ```JAVA 2 | package easy_test; 3 | 4 | /** 公众号:「一个不甘平凡的码农」 5 | * 1) 数组的插入、删除、按照下标随机访问操作; 6 | * 2)数组中的数据是int类型的; 7 | * 8 | * Author:小鹿 9 | */ 10 | /** 11 | * 测试数据注意事项: 12 | * 13 | * 插入数据: 14 | * 1、判断空数组插入数据。 15 | * 2、输入下标索引超出下标范围。 16 | * 3、将数据插入到尾部。 17 | * 18 | * 查找数据: 19 | * 1、输入下标超出下标范围 20 | * 21 | * 22 | * 删除数据: 23 | * 1、索引超出下标范围。 24 | * 2、删除尾元素 25 | * 26 | */ 27 | 28 | 29 | /** 30 | * 1) 数组的插入、删除、按照下标随机访问操作; 31 | * 2)数组中的数据是int类型的; 32 | * 33 | */ 34 | 35 | public class Array05 { 36 | 37 | //声明变量 38 | private int data[]; 39 | private int n; 40 | private int count; 41 | 42 | public static void main(String[] args) { 43 | Array05 array = new Array05(4); 44 | //插入数据 45 | array.insertHead(1); 46 | System.out.println("插入数据:"+array.insert(1, 2)); 47 | array.insert(2, 3); 48 | array.insert(3, 4); 49 | //查找 50 | System.out.println("查找数据:"+array.find(1)); 51 | //删除数据 52 | System.out.println("删除数据:"+array.delete(4)); 53 | //打印所有数据 54 | System.out.println("打印所有数据:"); 55 | array.printAll(); 56 | 57 | } 58 | 59 | /** 60 | * @param capacity:用户传参,数组的大小 61 | * 功能:构造函数(初始化数据) 62 | */ 63 | public Array05(int capacity) { 64 | //定义一个大小为 capacity 的数组 65 | data = new int[capacity]; 66 | n = capacity; 67 | count = 0; 68 | } 69 | 70 | /** 71 | * 功能:插入头元素 72 | * @param value 插入的元素值 73 | * @return 74 | */ 75 | public Boolean insertHead(int value) { 76 | data[count] = value; 77 | count++; 78 | return true; 79 | } 80 | 81 | /** 82 | * 功能:数组插入元素 83 | * @param index:数组下标索引 84 | * @param value:要插入的元素值 85 | * @return 86 | */ 87 | public boolean insert(int index, int value) { 88 | //首先判断删除的索引值是否在数组索引范围内(边界问题) 89 | if (index < 0 || index > count) return false; 90 | //还要考虑到一种情况就是,如果你一直删除元素知道把元素全部删除完,数组长度为0,无法进行插入元素,对于这种情况就需要进行判断 91 | if (count == n) { 92 | return false; 93 | } 94 | //数组中数据从最后一依次向后移动,直到将用户指定索引元素空出空间 95 | for (int i = count - 1; i >= index; --i) { 96 | data[i+1] = data[i]; 97 | } 98 | //将元素插入到数组中 99 | data[index] = value; 100 | //数组长度+1 101 | ++count; 102 | return true; 103 | } 104 | 105 | 106 | /** 107 | * 功能:下标随机访问 108 | * @param index:用户传参下标 109 | * @return 110 | */ 111 | public int find(int index) { 112 | //索引判断,课程中所讲的边界问题(不在数组的范围内函数返回-1) 113 | if (index < 0 || index > count ) return -1; 114 | //否则返回该索引对应的数据 115 | return data[index-1]; 116 | } 117 | 118 | /** 119 | * 功能:根据用户输入索引删除数组中数据。 120 | * @param index 121 | * @return 122 | */ 123 | public boolean delete(int index) { 124 | //首先判断删除的索引值是否在数组索引范围内(边界问题) 125 | if (index < 0 || index > count) return false; 126 | //将删除元素的后边元素都向前依次移动 127 | for(int i = index; i < count; ++i) { 128 | data[i-1] = data[i]; 129 | } 130 | //删除一个元素后,数组长度 -1 131 | --count; 132 | return true; 133 | } 134 | 135 | /** 136 | * 通过for循环输出数组所有元素 137 | */ 138 | public void printAll() { 139 | for (int i = 0; i < count; ++i) { 140 | System.out.print(data[i] + " "); 141 | } 142 | System.out.println(); 143 | } 144 | } 145 | 146 | 147 | ``` 148 | 149 | -------------------------------------------------------------------------------- /Link_List/javascript/反转链表.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * 2019/3/24 5 | * @author 小鹿 6 | * 功能:反转链表 7 | */ 8 | 9 | //定义结点 10 | class Node{ 11 | constructor(data){ 12 | this.data = data; 13 | this.next = null; 14 | } 15 | } 16 | 17 | //定义链表 18 | class LinkedList{ 19 | constructor(){ 20 | this.head = new Node('head'); 21 | } 22 | 23 | //根据 value 查找结点 24 | findByValue = (value) =>{ 25 | let currentNode = this.head; 26 | while(currentNode !== null && currentNode.data !== value){ 27 | currentNode = currentNode.next; 28 | } 29 | //判断该结点是否找到 30 | console.log(currentNode) 31 | return currentNode === null ? -1 : currentNode; 32 | } 33 | 34 | //根据 index 查找结点 35 | findByIndex = (index) =>{ 36 | let pos = 0; 37 | let currentNode = this.head; 38 | while(currentNode !== null && pos !== index){ 39 | currentNode = currentNode.next; 40 | pos++; 41 | } 42 | //判断是否找到该索引 43 | console.log(currentNode) 44 | return currentNode === null ? -1 : currentNode; 45 | } 46 | 47 | //插入元素(指定元素向后插入) 48 | insert = (value,element) =>{ 49 | //先查找该元素 50 | let currentNode = this.findByValue(element); 51 | //如果没有找到 52 | if(currentNode == -1){ 53 | console.log("未找到插入位置!") 54 | return; 55 | } 56 | let newNode = new Node(value); 57 | newNode.next = currentNode.next; 58 | currentNode.next = newNode; 59 | } 60 | 61 | //根据值删除结点 62 | delete = (value) =>{ 63 | let currentNode = this.head; 64 | let preNode = null; 65 | while(currentNode !== null && currentNode.data !== value){ 66 | preNode = currentNode; 67 | currentNode = currentNode.next; 68 | } 69 | if(currentNode == null) return -1; 70 | preNode.next = currentNode.next; 71 | } 72 | 73 | //遍历所有结点 74 | print = () =>{ 75 | let currentNode = this.head 76 | //如果结点不为空 77 | while(currentNode !== null){ 78 | console.log(currentNode.data) 79 | currentNode = currentNode.next; 80 | } 81 | } 82 | 83 | // 功能:单链表反转 84 | // 步骤: 85 | // 1、定义三个指针(pre=null/next/current) 86 | // 2、判断链表是否可反转(头节点是否为空、是否有第二个结点) 87 | // 3、尾指针指向第一个结点的 next 88 | // 4、尾指针向前移动 89 | // 5、当前指针(current)向后移动 90 | // 6、将 head 指向单转好的结点 91 | reverseList = () =>{ 92 | //声明三个指针 93 | let current = this.head; //当前指针指向头节点 94 | let pre = null;//尾指针 95 | let next;//指向当前指针的下一个指针 96 | 97 | //判断单链表是否符合反转的条件(一个结点以上)? 98 | if(this.head == null || this.head.next == null) return -1; 99 | 100 | //开始反转 101 | while(current !== null){ 102 | next = current.next; 103 | current.next = pre; 104 | pre = current; 105 | current = next; 106 | } 107 | this.head = pre; 108 | } 109 | } 110 | 111 | // 测试 112 | const list = new LinkedList(); 113 | list.insert('1','head'); 114 | list.insert('2','1'); 115 | list.insert('3','2'); 116 | list.insert('4','3'); 117 | list.insert('5','4'); 118 | list.insert('6','5'); 119 | list.print(); 120 | console.log('--------------------反转链表---------------------') 121 | list.reverseList() 122 | list.print() 123 | 124 | ``` 125 | 126 | -------------------------------------------------------------------------------- /Link_List/javascript/删除倒数第 K 个结点.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 2019/4/25 4 | * 公众号:「一个不甘平凡的码农」 5 | * @author 小鹿 6 | * 功能:删除倒数第 K 个结点 7 | * 8 | */ 9 | 10 | //定义结点 11 | class Node{ 12 | constructor(data){ 13 | this.data = data; 14 | this.next = null; 15 | } 16 | } 17 | 18 | //定义链表 19 | class LinkedList{ 20 | constructor(){ 21 | this.head = new Node('head'); 22 | } 23 | 24 | //根据 value 查找结点 25 | findByValue = (value) =>{ 26 | let currentNode = this.head; 27 | while(currentNode !== null && currentNode.data !== value){ 28 | currentNode = currentNode.next; 29 | } 30 | //判断该结点是否找到 31 | console.log(currentNode) 32 | return currentNode === null ? -1 : currentNode; 33 | } 34 | 35 | //根据 index 查找结点 36 | findByIndex = (index) =>{ 37 | let pos = 0; 38 | let currentNode = this.head; 39 | while(currentNode !== null && pos !== index){ 40 | currentNode = currentNode.next; 41 | pos++; 42 | } 43 | //判断是否找到该索引 44 | console.log(currentNode) 45 | return currentNode === null ? -1 : currentNode; 46 | } 47 | 48 | //插入元素(指定元素向后插入) 49 | insert = (value,element) =>{ 50 | //先查找该元素 51 | let currentNode = this.findByValue(element); 52 | //如果没有找到 53 | if(currentNode == -1){ 54 | console.log("未找到插入位置!") 55 | return; 56 | } 57 | let newNode = new Node(value); 58 | newNode.next = currentNode.next; 59 | currentNode.next = newNode; 60 | } 61 | 62 | //根据值删除结点 63 | delete = (value) =>{ 64 | let currentNode = this.head; 65 | let preNode = null; 66 | while(currentNode !== null && currentNode.data !== value){ 67 | preNode = currentNode; 68 | currentNode = currentNode.next; 69 | } 70 | if(currentNode == null) return -1; 71 | preNode.next = currentNode.next; 72 | } 73 | 74 | //遍历所有结点 75 | print = () =>{ 76 | let currentNode = this.head 77 | //如果结点不为空 78 | while(currentNode !== null){ 79 | console.log(currentNode.data) 80 | currentNode = currentNode.next; 81 | } 82 | } 83 | 84 | //步骤: 85 | // 1、检测链表是否为环? 86 | // 2、反转单链表? 87 | // 3、判断是否能够找到该索引的结点? 88 | // 4、没有找到直接返回,找到直接{按值删除} 89 | // 5、删除完再进行链表反转 90 | removeByIndexFromEnd = (index) =>{ 91 | // 检测是否为环 92 | if(this.checkCicle()) return false; 93 | // 单链表反转 94 | this.reverseList() 95 | 96 | let pos = 1; 97 | let currentNode = this.head.next; 98 | while(currentNode !== null && pos < index){ 99 | currentNode = currentNode.next; 100 | pos ++; 101 | } 102 | 103 | if(currentNode == null){ 104 | console.log('没有找到该索引对应的结点'); 105 | return false; 106 | }else{ 107 | //删除该结点数据 108 | this.delete(currentNode.data); 109 | //再进行链表反转 110 | this.reverseList(); 111 | } 112 | } 113 | } 114 | // 测试 115 | const list = new LinkedList(); 116 | list.insert('1','head'); 117 | list.insert('2','1'); 118 | list.insert('3','2'); 119 | list.insert('4','3'); 120 | list.insert('5','4'); 121 | list.insert('6','5'); 122 | list.print(); 123 | console.log('----------------删除倒数第 k 的元素---------------') 124 | list.removeByIndexFromEnd(2) 125 | list.print() 126 | ``` 127 | 128 | -------------------------------------------------------------------------------- /Link_List/javascript/DoubleLinkedList.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 时间:2019/3/31 4 | * 双链表的插入、删除 5 | * 功能: 6 | * 1)双链表的尾部插入 7 | * 2)双链表的按位置插入 8 | * 3)双链表的按位置删除 9 | * @author 小鹿 10 | * 11 | */ 12 | 13 | //定义结点 14 | class Node{ 15 | constructor(data){ 16 | this.data = data; 17 | this.pre = null; 18 | this.next = null; 19 | } 20 | } 21 | 22 | //双向链表 23 | class DoublyLinkedList{ 24 | constructor(){ 25 | this.head = null; 26 | //链表的长度 27 | this.length = 0; 28 | //双链表尾指针 29 | this.tail = null; 30 | } 31 | 32 | //添加元素到双向链表的尾部 33 | insertTailByValue = (value)=>{ 34 | let newNode = new Node(value); 35 | let current = this.head; 36 | 37 | //判断当前双链表是否为 null 38 | if(this.head == null){ 39 | this.head = newNode; 40 | this.tail = newNode; 41 | }else{ 42 | //遍历找到链尾 43 | while(current.next !== null){ 44 | current = current.next; 45 | } 46 | //将结点添加到尾部 47 | current.next = newNode; 48 | //新结点的前结点指向 node 49 | newNode.pre = current; 50 | //尾指针指向尾结点 51 | this.tail = newNode; 52 | } 53 | this.length++; 54 | return true; 55 | } 56 | 57 | // 添加元素到指定元素后 58 | // 参数一:添加的元素 59 | // 参数二:指定的位置 60 | insertDataByValue = (value,position)=>{ 61 | let newNode = new Node(value); 62 | let current = this.head; 63 | let previous = null; 64 | let index = 0; 65 | 66 | //position 的边界条件 67 | if(position < 0 && position >= this.length){ 68 | return false; 69 | } 70 | //如果是 0 索引,就插入头部 71 | if(position == 0){ 72 | //新结点的尾指向头结点 73 | newNode.next = this.head; 74 | //头结点的前结点指向新结点 75 | this.head.pre = newNode; 76 | //将插入结点设置为新结点 77 | this.head = newNode; 78 | }else if(position === this.length){ 79 | //将新结点插入尾部 80 | this.tail.next = newNode; 81 | newNode.pre = this.tail; 82 | this.tail = newNode; 83 | }else{ 84 | while(index !== position){ 85 | //记录插入节点的前一个节点 86 | previous = current; 87 | current = current.next; 88 | index++; 89 | } 90 | //绑定插入结点和后一个结点的关系 91 | newNode.next = current; 92 | current.pre = newNode; 93 | //绑定插入节点和前一个节点的关系 94 | previous.next = newNode; 95 | newNode.pre = previous; 96 | } 97 | this.length++; 98 | return true; 99 | } 100 | 101 | // 移除双向链表中某个位置的元素 102 | removeByValue = (position)=>{ 103 | let current = this.head; 104 | let index = 0; 105 | let previous = null; 106 | 107 | //position 的边界条件 108 | if(position < 0 && position >= this.length){ 109 | return false; 110 | } 111 | 112 | // 移除链表的头部 113 | if(position === 0){ 114 | this.head = current.next; 115 | this.head.pre = null; 116 | }else if(position === this.length){ 117 | // 移除尾部结点 118 | current = this.tail; 119 | this.tail = current.pre; 120 | this.tail.next = null; 121 | }else{ 122 | //移除中间结点元素 123 | while(index !== position){ 124 | previous = current; 125 | current = current.next; 126 | } 127 | previous.next = current.next; 128 | current.next.pre = previous; 129 | } 130 | this.length--; 131 | return current.data; 132 | } 133 | 134 | //打印双链表 135 | display = ()=>{ 136 | let p = this.head; 137 | while(p !== null){ 138 | console.log(p.data); 139 | p = p.next; 140 | } 141 | } 142 | } 143 | 144 | //测试 145 | let list = new DoublyLinkedList(); 146 | console.log('---------------------插入数据------------------------') 147 | list.insertTailByValue(1); 148 | list.insertTailByValue(2); 149 | list.insertTailByValue(3); 150 | list.insertTailByValue(4); 151 | list.display(); 152 | console.log('-------------------插入数据(位置)--------------------') 153 | list.insertDataByValue(5,4); 154 | list.insertDataByValue(6,0); 155 | list.display(); 156 | console.log('---------------------删除数据------------------------') 157 | list.removeByValue(0); 158 | list.removeByValue(5); 159 | list.display(); 160 | ``` 161 | 162 | -------------------------------------------------------------------------------- /Link_List/javascript/两个有序链表的合并.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | 3 | /** 4 | * 公众号:「一个不甘平凡的码农」 5 | * 2019/3/24 6 | * @author 小鹿 7 | * 功能:两个有序链表的合并 8 | * 1)普通合并 9 | * 2)递归合并 10 | */ 11 | 12 | //定义结点 13 | class Node{ 14 | constructor(data){ 15 | this.data = data; 16 | this.next = null; 17 | } 18 | } 19 | 20 | //定义链表 21 | class LinkedList{ 22 | constructor(){ 23 | this.head = new Node('head'); 24 | } 25 | 26 | //根据 value 查找结点 27 | findByValue = (value) =>{ 28 | let currentNode = this.head; 29 | while(currentNode !== null && currentNode.data !== value){ 30 | currentNode = currentNode.next; 31 | } 32 | //判断该结点是否找到 33 | console.log(currentNode) 34 | return currentNode === null ? -1 : currentNode; 35 | } 36 | 37 | //根据 index 查找结点 38 | findByIndex = (index) =>{ 39 | let pos = 0; 40 | let currentNode = this.head; 41 | while(currentNode !== null && pos !== index){ 42 | currentNode = currentNode.next; 43 | pos++; 44 | } 45 | //判断是否找到该索引 46 | console.log(currentNode) 47 | return currentNode === null ? -1 : currentNode; 48 | } 49 | 50 | //插入元素(指定元素向后插入) 51 | insert = (value,element) =>{ 52 | //先查找该元素 53 | let currentNode = this.findByValue(element); 54 | //如果没有找到 55 | if(currentNode == -1){ 56 | console.log("未找到插入位置!") 57 | return; 58 | } 59 | let newNode = new Node(value); 60 | newNode.next = currentNode.next; 61 | currentNode.next = newNode; 62 | } 63 | 64 | //根据值删除结点 65 | delete = (value) =>{ 66 | let currentNode = this.head; 67 | let preNode = null; 68 | while(currentNode !== null && currentNode.data !== value){ 69 | preNode = currentNode; 70 | currentNode = currentNode.next; 71 | } 72 | if(currentNode == null) return -1; 73 | preNode.next = currentNode.next; 74 | } 75 | 76 | //遍历所有结点 77 | print = () =>{ 78 | let currentNode = this.head 79 | //如果结点不为空 80 | while(currentNode !== null){ 81 | console.log(currentNode.data) 82 | currentNode = currentNode.next; 83 | } 84 | } 85 | } 86 | // 测试 87 | sortedList1 = new LinkedList() 88 | sortedList1.insert(9, 'head') 89 | sortedList1.insert(8, 'head') 90 | sortedList1.insert(7, 'head') 91 | sortedList1.insert(6, 'head') 92 | sortedList2 = new LinkedList() 93 | sortedList2.insert(21, 'head') 94 | sortedList2.insert(20, 'head') 95 | sortedList2.insert(19, 'head') 96 | sortedList2.insert(18, 'head') 97 | console.log('----------------合并两个有序的链表----------------') 98 | let resultList = mergeSortList(sortedList1.head.next,sortedList2.head.next) 99 | while (resultList !== null) { 100 | console.log(resultList.date); 101 | resultList = resultList.next; 102 | } 103 | ``` 104 | 105 | > 法一:普通合并 106 | 107 | ```javascript 108 | // 功能:两个有序链表的合并 109 | // 步骤: 110 | // 1、判断两个链表是否为 null,并将链表赋予临时变量 111 | // 2、声明合并链表,通过 currentNode 指向当前结点 112 | // 3、两个链表比较大小,数值小的添加到合并链表中,合并链表进行指针移动 113 | // 4、将链表剩余数据添加到合并链表后边 114 | const mergeSortList = (listA,listB) =>{ 115 | //判断链表是否为空 116 | if(listA === null) return false; 117 | if(listB === null) return false; 118 | let a = listA; 119 | let b = listB; 120 | 121 | //声明合并链表,通过 currentNode 指向当前结点 122 | let resultList = undefined 123 | 124 | //两个链表比较大小,数值小的添加到合并链表中,合并链表进行指针移动 125 | if (a.data < b.data) { 126 | resultList = a 127 | a = a.next 128 | } else { 129 | resultList = b 130 | b = b.next 131 | } 132 | let currentNode = resultList; 133 | while (a !== null && b !== null) { 134 | if (a.data < b.data) { 135 | currentNode.next = a 136 | a = a.next 137 | } else { 138 | currentNode.next = b 139 | b = b.next 140 | } 141 | currentNode = currentNode.next 142 | } 143 | 144 | // 将链表剩余数据添加到合并链表后边 145 | if(a !== null){ 146 | currentNode.next = a; 147 | }else{ 148 | currentNode.next = b; 149 | } 150 | //返回合并链表 151 | return resultList; 152 | } 153 | ``` 154 | 155 | > 法二:递归合并 156 | 157 | ```javascript 158 | let result = null; 159 | //终止条件 160 | if(l1 == null) return l2; 161 | if(l2 == null) return l1; 162 | 163 | //判断数值大小递归 164 | if(l1.val < l2.val){ 165 | result = l1; 166 | result.next = mergeTwoLists(l1.next,l2); 167 | }else{ 168 | result = l2; 169 | result.next = mergeTwoLists(l2.next,l1); 170 | } 171 | 172 | //返回结果 173 | return result; 174 | ``` 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /Find/Java/BinarySearch.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | /** 5 | * 公众号:「一个不甘平凡的码农 」 6 | * 二分查找 7 | * @author 小鹿 8 | * 9 | */ 10 | public class TwoFind { 11 | 12 | /** 13 | * 思路: 14 | * 1、在数组头、尾定义两个指针(low,height) 15 | * 2、取中间数值mid 16 | * 3、判断是否为查找值 17 | * 4、如果不是,移动 mid 指针,变化low,height 18 | * @param args 19 | */ 20 | public static void main(String[] args) { 21 | int[] a = {3,4,6,7,10}; 22 | TwoFind tFind = new TwoFind(); 23 | System.out.print("该数据的下标为:"+tFind.find4(a,5,5)); 24 | } 25 | 26 | /** 27 | * 功能:最简单的二分查找 28 | * @param a 数组 29 | * @param n 数组长度 30 | * @param value 要查找的值 31 | */ 32 | public int simpleFind(int[] a,int n, int value) { 33 | int low = 0; 34 | int high = n - 1; 35 | 36 | // 当两个指针同时指向一个数据时,必须用 <= 37 | while(low <= high) { 38 | //注意:两者之和可能会溢出 39 | //改进: low + (high - low)/2 或 low + ((high - low)>>2) 40 | int mid = low + (high - low)/2; 41 | if(a[mid] == value) { 42 | return mid; 43 | }else if(a[mid] < value) { 44 | // 如果不 + 或 - 会发生死循环 45 | low = mid + 1; 46 | }else { 47 | high = mid - 1; 48 | } 49 | } 50 | return -1; 51 | } 52 | 53 | /** 54 | * 功能:递归实现最简单的二分查找 55 | * @param a 数据 56 | * @param low 初始位置 57 | * @param height 终止位置 58 | * @param value 查找的值 59 | * @return 60 | */ 61 | public int recursionTwoFind(int[] a,int low,int high,int value) { 62 | //终止条件 63 | if(low > high) return -1; 64 | 65 | //计算中间结点 66 | int mid = low + (high-low)/2; 67 | 68 | //进行递归 69 | if(a[mid] == value) { 70 | return mid; 71 | }else if(a[mid] < value){ 72 | return recursionTwoFind(a, mid + 1, high, value); 73 | }else { 74 | return recursionTwoFind(a, low, mid - 1, value); 75 | } 76 | } 77 | 78 | /** 79 | * 变体一:查找第一个值等于给定值的元素 80 | * @param a 数组 81 | * @param n 数组的长度 82 | * @param value 超找的值 83 | * @return 该元素的数组下标 84 | */ 85 | public int find1(int[] a, int n, int value) { 86 | int low = 0; 87 | int high = n - 1; 88 | 89 | // 当两个指针同时指向一个数据时,必须用 <= 90 | while(low <= high) { 91 | //注意:两者之和可能会溢出 92 | //改进: low + (high - low)/2 或 low + ((high - low)>>2) 93 | int mid = low + (high - low)/2; 94 | if(a[mid] == value) { 95 | // 找出重复元素中第一个元素 96 | if(mid == 0 || a[mid - 1] != value) { 97 | return mid; 98 | }else { 99 | high = mid - 1; 100 | } 101 | }else if(a[mid] < value) { 102 | // 如果不 + 或 - 会发生死循环 103 | low = mid + 1; 104 | }else { 105 | high = mid - 1; 106 | } 107 | } 108 | return -1; 109 | } 110 | 111 | /** 112 | * 变体二:查找最后一个值等于给定值的元素 113 | * @param a 数组 114 | * @param n 数组的长度 115 | * @param value 超找的值 116 | * @return 该元素的数组下标 117 | */ 118 | public int find2(int[] a, int n, int value) { 119 | int low = 0; 120 | int high = n - 1; 121 | 122 | // 当两个指针同时指向一个数据时,必须用 <= 123 | while(low <= high) { 124 | //注意:两者之和可能会溢出 125 | //改进: low + (high - low)/2 或 low + ((high - low)>>2) 126 | int mid = low + (high - low)/2; 127 | if(a[mid] == value) { 128 | // 找出重复元素中第一个元素 129 | if(mid == n - 1 || a[mid + 1] != value) { 130 | return mid; 131 | }else { 132 | low = mid + 1; 133 | } 134 | }else if(a[mid] < value) { 135 | // 如果不 + 或 - 会发生死循环 136 | low = mid + 1; 137 | }else { 138 | high = mid - 1; 139 | } 140 | } 141 | return -1; 142 | } 143 | 144 | /** 145 | * 变体三:查找第一个大于等于给定值的元素 146 | * @param a 数组 147 | * @param n 数组的长度 148 | * @param value 数组的值 149 | * @return 150 | */ 151 | public int find3(int[] a, int n, int value) { 152 | int low = 0; 153 | int high = n - 1; 154 | 155 | // 当两个指针同时指向一个数据时,必须用 <= 156 | while(low <= high) { 157 | // 注意:两者之和可能会溢出 158 | // 改进: low + (high - low)/2 或 low + ((high - low)>>2) 159 | int mid = low + (high - low)/2; 160 | if(a[mid] >= value) { 161 | if((mid == 0) || (a[mid - 1] < value )) { 162 | return mid; 163 | }else { 164 | high = mid - 1; 165 | } 166 | }else { 167 | low = mid + 1; 168 | } 169 | } 170 | return -1; 171 | } 172 | 173 | /** 174 | * 变体四:查找第一个小于等于给定值的元素 175 | * @param a 数组 176 | * @param n 数组的长度 177 | * @param value 数组的值 178 | * @return 179 | */ 180 | public int find4(int[] a, int n, int value) { 181 | int low = 0; 182 | int high = n - 1; 183 | 184 | // 当两个指针同时指向一个数据时,必须用 <= 185 | while(low <= high) { 186 | // 注意:两者之和可能会溢出 187 | // 改进: low + (high - low)/2 或 low + ((high - low)>>2) 188 | int mid = low + (high - low)/2; 189 | if(a[mid] <= value) { 190 | if((mid == n-1) || (a[mid + 1] > value )) { 191 | return mid; 192 | }else { 193 | low = mid + 1; 194 | } 195 | }else { 196 | high = mid - 1; 197 | } 198 | } 199 | return -1; 200 | } 201 | } 202 | 203 | ``` 204 | 205 | -------------------------------------------------------------------------------- /Find/JavaScript/binarySearch.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 公众号:「一个不甘平凡的码农」 4 | * 2019/3/16 5 | * 功能:最简单的二分查找 6 | * 算法思路: 7 | * 1、声明两个指针分别指向头和尾(low,high) 8 | * 2、取中间数值 mid 9 | * 3、判断是否为查找值 10 | * 4、如果不是,移动 mid 指针,变化low,height 11 | * 边界条件: 12 | * 1)判断数组是否为空 13 | * 2)两个指针的位置大小关系 14 | * @param arr 一组数据 15 | * @param target 查找的目标值 16 | */ 17 | const binarySearch = (arr,target) => { 18 | //如果数组没有数据,直接返回-1 19 | if(arr.length === 0) return -1; 20 | //声明两个指针分别指向头和尾(low,high) 21 | let low = 0 22 | let high = arr.length - 1; 23 | //判断两个指针的位置关系 24 | while(low <= high){ 25 | //取中间值 26 | const mid = Math.floor((low + high) / 2); 27 | //判断是否为查找元素 28 | if(arr[mid] === target){ 29 | return mid; 30 | //左移动 mid 指针 31 | }else if(target < arr[mid]){ 32 | high = mid - 1; 33 | //右移动 mid 指针 34 | }else{ 35 | low = mid + 1; 36 | } 37 | } 38 | return -1; 39 | } 40 | //测试 41 | const arr = [1, 4, 5, 6, 7, 8, 10, 11, 23, 42, 44, 54, 56, 77, 102]; 42 | console.log(binarySearch(arr,24)); 43 | 44 | 45 | /** 46 | * 2019/3/16 47 | * 变体一:查找第一个值等于给定元素 48 | * 算法思路: 49 | * 1、如果查找到的该值等于目标元素 50 | * 2、判断该数据前一个元素不等于给定元素当前元素就是查找的第一个值 51 | * 3、当查找到的元素为第一个元素时,无需向前判断查找了 52 | * 4、否则改变 high 指针向前查找 53 | * @param arr 一组数据 54 | * @param target 查找的目标值 55 | */ 56 | const binarySearch1 = (arr,target) => { 57 | //如果数组没有数据,直接返回-1 58 | if(arr.length === 0) return -1; 59 | //声明两个指针分别指向头和尾(low,high) 60 | let low = 0 61 | let high = arr.length - 1; 62 | while(low <= high){ 63 | //取中间值 64 | const mid = Math.floor((low + high) / 2); 65 | if(arr[mid] === target){ 66 | // mid 不为第一个元素且 mid 前方无相同元素 67 | if(mid == 0 || target != arr[mid-1]){ 68 | return mid; 69 | }else{ 70 | high = mid - 1; 71 | } 72 | }else if(target < arr[mid]){ 73 | high = mid - 1; 74 | }else{ 75 | low = mid + 1; 76 | } 77 | } 78 | return -1; 79 | } 80 | //测试 81 | const arr = [1, 4, 5, 6, 7, 8, 10, 10, 11, 23, 23, 23, 42, 42, 44, 54, 56, 77, 102]; 82 | console.log(binarySearch1(arr,42)); 83 | 84 | /** 85 | * 2019/3/16 86 | * 变体二:查找最后一个值等于给定元素 87 | * 算法思路: 88 | * 1、如果查找到的该值等于目标元素 89 | * 2、判断该数据后一个元素不等于给定元素当前元素就是查找的最后一个值 90 | * 3、当查找到的元素为最后一个元素时,无需向后判断查找了 91 | * 4、否则改变 low 指针向后查找 92 | * @param arr 一组数据 93 | * @param target 查找的目标值 94 | */ 95 | const binarySearch2 = (arr,target) => { 96 | //如果数组没有数据,直接返回-1 97 | if(arr.length === 0) return -1; 98 | 99 | let low = 0 100 | let high = arr.length - 1; 101 | while(low <= high){ 102 | const mid = Math.floor((low + high) / 2); 103 | if(arr[mid] === target){ 104 | // mid 不为最后元素且 mid 后方无相同元素 105 | if(mid == arr.length-1 || target != arr[mid + 1]){ 106 | return mid; 107 | }else{ 108 | low = mid + 1; 109 | } 110 | }else if(target < arr[mid]){ 111 | high = mid - 1; 112 | }else{ 113 | low = mid + 1; 114 | } 115 | } 116 | return -1; 117 | } 118 | //测试 119 | const arr = [1, 4, 5, 6, 7, 8, 10, 10, 11, 23, 23, 23, 42, 42, 44, 54, 56, 77, 102]; 120 | console.log(binarySearch2(arr,42)); 121 | 122 | /** 123 | * 2019/3/16 124 | * 变体三:查找第一个小于等于指定该元素的值 125 | * 算法思路: 126 | * 1、如果查找到的该值小于等于目标元素 127 | * 2、如果为最后一个元素,已经达到最小,因为左边是更小的元素 128 | * 3、如果右边的值该元素右边的值大于给定元速,说明该值就是该查找值 129 | * 4、否则改变 low 指针向后查找 130 | * @param arr 一组数据 131 | * @param target 查找的目标值 132 | */ 133 | const binarySearch3 = (arr,target) => { 134 | //如果数组没有数据,直接返回-1 135 | if(arr.length === 0) return -1; 136 | 137 | let low = 0 138 | let high = arr.length - 1; 139 | while(low <= high){ 140 | const mid = Math.floor((low + high) / 2); 141 | //满足条件:小于等于目标值 142 | if(arr[mid] <= target){ 143 | //如果为最后一个元素,已经达到最小,因为左边是更小的元素 144 | //如果右边的值该元素右边的值大于给定元速,说明该值就是该查找值 145 | if(mid == arr.length - 1 || arr[mid + 1] > target){ 146 | return mid; 147 | }else{ 148 | //否则向右侧查找 149 | low = mid + 1; 150 | } 151 | }else{ 152 | high = mid - 1; 153 | } 154 | } 155 | return -1; 156 | } 157 | //测试 158 | const arr = [1, 4, 5, 6, 7, 8, 10, 11, 23, 42, 44, 54, 56, 77, 102]; 159 | console.log(binarySearch3(arr,12)); 160 | 161 | /** 162 | * 2019/3/16 163 | * 变体四:查找第一个大于等于指定该元素的值 164 | * 算法思路: 165 | * 1、如果查找到的该值大于等于目标元素 166 | * 2、如果为最后一个元素,已经达到最小第一个,因为左边是更小的元素 167 | * 3、如果右边的值该元素右边的值大于给定元速,说明该值就是该查找值 168 | * 4、否则改变 high 指针向前查找 169 | * @param arr 一组数据 170 | * @param target 查找的目标值 171 | */ 172 | const binarySearch4 = (arr,target) => { 173 | //如果数组没有数据,直接返回-1 174 | if(arr.length === 0) return -1; 175 | 176 | let low = 0 177 | let high = arr.length - 1; 178 | while(low <= high){ 179 | const mid = Math.floor((low + high) / 2); 180 | //如果查找到的该值大于等于目标元素 181 | //如果为第一个元素,是第一个最大的元素,因为右边是更大的元素 182 | if(arr[mid] >= target){ 183 | if(mid == 0 || arr[mid - 1] < target){ 184 | return mid; 185 | }else{ 186 | //否则向左侧查找 187 | high = mid - 1; 188 | } 189 | }else{ 190 | low = mid + 1; 191 | } 192 | } 193 | return -1; 194 | } 195 | //测试 196 | const arr = [1, 4, 5, 6, 7, 8, 10, 11, 23, 42, 44, 54, 56, 77, 102]; 197 | console.log(binarySearch4(arr,24)); 198 | ``` 199 | 200 | -------------------------------------------------------------------------------- /查询IP地址归属地/查询IP地址归属地.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20万条数据快速定位IP地址的归属地 9 | 29 | 30 | 31 |
32 |

模拟20万数据快速定位IP地址的归属地

33 |
34 | 请输入IP地址: 35 |
36 |
37 |

查询结果为: 山东省 潍坊市 电信

38 |
39 |
40 | 41 | 184 | 185 | ``` 186 | 187 | -------------------------------------------------------------------------------- /Tree/JAVA/二叉查找树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.xiaolu.Tree; 3 | 4 | 5 | /** 6 | * 公众号:「一个不甘平凡的码农」 7 | * 时间:2019/2/24 8 | * 二叉查找树的增、删、改、查 9 | * 功能: 10 | * 1)插入数据 11 | * 2)查找数据 12 | * 3)删除数据 13 | * 4)查找数据 14 | * 5)查找最大值 15 | * 6)查找最小值 16 | * 7)前、中、后序遍历 17 | * @author 小鹿 18 | * 19 | */ 20 | public class BinarySearchTree { 21 | 22 | public static void main(String[] args) { 23 | BinarySearchTree b = new BinarySearchTree(); 24 | //插入数据 25 | b.insert(13); 26 | b.insert(8); 27 | b.insert(18); 28 | b.insert(6); 29 | b.insert(10); 30 | b.insert(16); 31 | b.insert(20); 32 | 33 | //前序遍历数据 34 | b.inorderTraversal(tree); 35 | 36 | System.out.println(" "); 37 | //查找数据 38 | System.out.println("查找数据:" + b.find(16)); 39 | //求最大值 40 | System.out.println("树中的最大值为:"+b.findMax().data); 41 | //求最小值 42 | System.out.println("树中的最小值为:"+b.findMin().data); 43 | //删除数据 44 | System.out.println("删除数据:"+20); 45 | b.delete(20); 46 | b.inorderTraversal(tree); 47 | } 48 | 49 | //声明一课树 50 | private static Node tree; 51 | 52 | //声明树节点 53 | public class Node{ 54 | //树节点的数据域 55 | private int data; 56 | //左子树 57 | private Node left; 58 | //右子树 59 | private Node right; 60 | 61 | public Node(int data) { 62 | this.data = data; 63 | } 64 | } 65 | 66 | /** 67 | * 时间:2019/2/24 68 | * 功能:查找数据 69 | * @param value 查找的数据 70 | * @return 返回节点 71 | */ 72 | public Node find(int value) { 73 | //遍历查找树节点 74 | Node p = tree; 75 | while(p != null) { 76 | //如果查找的数据大于根节点,左子树遍历 77 | if(p.data > value) { 78 | p = p.left; 79 | //否则,右子树遍历 80 | }else if(p.data < value) { 81 | p = p.right; 82 | //找到直接返回该节点 83 | }else { 84 | return p; 85 | } 86 | } 87 | return null; 88 | } 89 | 90 | /** 91 | * 时间:2019/2/24 92 | * 功能:插入数据 93 | * 边界条件: 94 | * 1、如果根节点为空,添加第一个节点 95 | * 2、 96 | * @param value 插入的数据 97 | * @return 98 | */ 99 | public Boolean insert(int value) { 100 | //如果树为空,新添加一个根节点 101 | if(tree == null) { 102 | tree = new Node(value); 103 | return true; 104 | } 105 | 106 | //否则,遍历树 107 | Node p = tree; 108 | while(p != null) { 109 | //判断是否大于根节点 110 | if(value > p.data) { 111 | //判断右子树是否为空 112 | if(p.right == null) { 113 | //添加右子树节点 114 | p.right = new Node(value); 115 | return true; 116 | } 117 | //如果不为空,则继续遍历右子树 118 | p = p.right; 119 | }else { 120 | //判断左子树是否为空 121 | if(p.left == null) { 122 | //添加左子树节点 123 | p.left = new Node(value); 124 | return true; 125 | } 126 | //如果不为空,则继续遍历左子树 127 | p = p.left; 128 | } 129 | } 130 | return false; 131 | } 132 | 133 | /** 134 | * 时间:2019/2/24 135 | * 功能:删除节点 136 | * 情况分析: 137 | * 1、只有根节点,删除根节点 138 | * 2、删除的节点无子节点或者只有一个节点 139 | * 3、删除的节点有两个子节点(找到右子树最小值,替换最小值进行删除) 140 | * @param data 要删除的数据 141 | */ 142 | public void delete(int value) { 143 | //指向删除的节点 144 | //a1 145 | Node p = tree; 146 | //记录 p 的父节点20,默认为 null 147 | //a1 148 | Node pp = null; 149 | 150 | //先寻找该数据的结点(根节点为删除节点) 151 | while(p != null && p.data != value) { 152 | //记录删除节点的父节点 153 | pp = p; 154 | if(value > p.data) { 155 | p = p.right; 156 | }else { 157 | p = p.left; 158 | } 159 | } 160 | 161 | //如果树为空树,直接返回 162 | if(p == null) return; 163 | 164 | //情况一:删除的节点有两个子节点 165 | if(p.left != null && p.right != null) { 166 | //查找右子树中最小的节点 167 | //在右子树没有子节点时,默认右子树为最小节点 168 | Node minP = p.right; 169 | //右子树的父节点就是 p 170 | Node minPP = p; 171 | //判断右子树是否有左子节点(左子节点小于根节点,最小值存在左子树中) 172 | while(minP.left != null) { 173 | //如果有子节点,最小值等于父节点 174 | minPP = minP; 175 | //最小节点变为,上一节点的左子树 176 | minP = minP.left; 177 | } 178 | //最小数与删除数据替换 179 | p.data = minP.data; 180 | //p 指向删除的结点 181 | p = minP; 182 | //父节点赋值给删除节点的父节点 183 | pp = minPP; 184 | } 185 | 186 | //准备对上边节点进行删除 187 | //情况二:删除的节点是叶子节点或者仅有一个节点 188 | Node child;// 记录 p 的子节点 189 | if(p.left != null) { 190 | child = p.left; 191 | }else if(p.right != null) { 192 | child = p.right; 193 | }else { 194 | //删除节点无节点的情况 195 | //a1 196 | child = null; 197 | } 198 | 199 | //根节点为删除节点,删除根节点 200 | if(pp == null) { 201 | //a1 202 | tree = child; 203 | }else if(pp.left == p) { 204 | pp.left = child; 205 | }else { 206 | pp.right = child; 207 | } 208 | } 209 | 210 | /** 211 | * 时间:2019/2/24 212 | * 功能:寻找最大节点 213 | */ 214 | public Node findMax() { 215 | //如果树为空树,直接返回 216 | if(tree == null) return null; 217 | //否则遍历右子树寻找最大点 218 | Node p = tree; 219 | while(p.right != null) { 220 | p = p.right; 221 | } 222 | return p; 223 | } 224 | 225 | /** 226 | * 时间:2019/2/24 227 | * 功能:寻找最小节点 228 | */ 229 | public Node findMin() { 230 | if(tree == null) return null; 231 | //否则遍历左子树寻找最大点 232 | Node p = tree; 233 | while(p.left != null) { 234 | p = p.left; 235 | } 236 | return p; 237 | } 238 | 239 | /** 240 | * 时间:2019/2/24 241 | * 功能:前序遍历 242 | * @param root 树的根节点 243 | */ 244 | public void preorderTraversal(Node root) { 245 | //如果树为空 246 | if(root == null) return; 247 | //根节点 248 | System.out.print(root.data + " "); 249 | //左子树 250 | inorderTraversal(root.left); 251 | //右子树 252 | inorderTraversal(root.right); 253 | 254 | } 255 | 256 | /** 257 | * 时间:2019/2/24 258 | * 功能:中序遍历 259 | * @param root 树的根节点 260 | */ 261 | public void inorderTraversal(Node root) { 262 | //如果树为空 263 | if(root == null) return; 264 | //左子树 265 | inorderTraversal(root.left); 266 | //根节点 267 | System.out.print(root.data + " "); 268 | //右子树 269 | inorderTraversal(root.right); 270 | 271 | } 272 | 273 | /** 274 | * 时间:2019/2/24 275 | * 功能:后序遍历 276 | * @param root 树的根节点 277 | */ 278 | public void postorderTraversal(Node root) { 279 | //如果树为空 280 | if(root == null) return; 281 | //左子树 282 | inorderTraversal(root.left); 283 | //右子树 284 | inorderTraversal(root.right); 285 | //根节点 286 | System.out.print(root.data + " "); 287 | 288 | } 289 | } 290 | ``` 291 | 292 | -------------------------------------------------------------------------------- /Link_List/JAVA/LinkedListAlgo07.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package easy_test; 3 | 4 | /** 5 | * 1) 单链表反转 6 | * 2) 链表中环的检测 7 | * 3) 两个有序的链表合并 8 | * 4) 删除链表倒数第n个结点 9 | * 5) 求链表的中间结点 10 | * 11 | * Author: 小鹿 12 | */ 13 | public class LinkedListAlgo { 14 | 15 | /** 16 | * 功能:定义 Node 结点,Node 包含数据域和引用(作用:存储所指向对象的内存地址) 17 | * @author Boy Baby 18 | * 19 | */ 20 | public static class Node { 21 | //数据域 22 | private int data; 23 | //引用(存储所指向对象的内存地址) 24 | private Node next; 25 | //构造函数(初始化数据) 26 | public Node(int data, Node next) { 27 | this.data = data; 28 | this.next = next; 29 | } 30 | //获取结点数据元素 31 | public int getData() { 32 | return data; 33 | } 34 | } 35 | //生成一个新的结点 36 | public static Node createNode(int value) { 37 | return new Node(value, null); 38 | } 39 | 40 | /** 41 | * 功能:单链表反转 42 | * 思路:创建一个头指针和尾指针,现将第一个结点拿出,放到做后一个节点,并将最后一个指针的值赋值给在链表中拿出的结点并向前移动指向刚拿出的结点地址。 43 | * 如果还存在没有遍历完的结点,头指针就向后移动,继续遍历。 44 | * @param list:传入要反转的链表 45 | * @return 46 | */ 47 | public static Node reverse(Node list) { 48 | //定义头结点 49 | Node headNode = null; 50 | //定义尾指针 51 | Node previousNode = null; 52 | //定义头指针并指向链表 53 | Node currentNode = list; 54 | //如果指针指向的链表不为空,就执行以下循环 55 | while (currentNode != null) { 56 | //先将指针指向的结点中存储的下一结点地址存储到 nextNode 中 57 | Node nextNode = currentNode.next; 58 | //判断指针指向该结点后是否还有结点,如果是 null 的话,就相当于指针处于链表的最后一个结点,而该结点的next是null(相当于为没有下一结点) 59 | if (nextNode == null) { 60 | //如果是最后一个结点,拿出来当做反转链表的头结点 61 | headNode = currentNode; 62 | } 63 | //将已经反转好的链表地址存储到在单链表中拆解下来的结点的地址域中 64 | currentNode.next = previousNode; 65 | //尾指针向前移动一个结点 66 | previousNode = currentNode; 67 | //头指针向后移动 68 | currentNode = nextNode; 69 | } 70 | return headNode; 71 | } 72 | 73 | /** 74 | * 功能:检测单链表是否为环 75 | * 思路:定义两个指针(快指针、慢指针),两个指针的初始化位置是fast指针在前,slow指针在后,每判断一个两个指针是否在同一个位置来检测环,如果不在同一位置 76 | * 就使fast指针向后走三步,慢指针向前走一步,每走一步就判断一下。如果指针遇到 null 的情况下,就说明单链表不为null。否则这样一直循环下去, 77 | * 直到两个指针重合。 78 | * @param list:传入要检测的单链表 79 | * @return 80 | */ 81 | public static boolean checkCircle(Node list) { 82 | // 如果单链表为空,直接返回false 83 | if (list == null) return false; 84 | //定义一个快指针 fast 85 | Node fast = list.next; 86 | //定义一个慢指针 slow 87 | Node slow = list; 88 | //判断两个指针指向的结点是否为空 89 | while (fast != null && fast.next != null) { 90 | //如果不为空就 fast 指针向后移动两个结点 91 | fast = fast.next.next; 92 | // solw 向后移动一个结点 93 | slow = slow.next; 94 | //如果两个指针重合,则返回 true 95 | if (slow == fast) return true; 96 | } 97 | //如果两个指针初始化就为 null,直接返回 false 98 | return false; 99 | } 100 | 101 | /** 102 | * 功能:单链表的合并 103 | * @param la:链表 la 104 | * @param lb:链表 lb 105 | * @return 106 | */ 107 | public static Node mergeSortedLists(Node la, Node lb) { 108 | //判断两个单链表是否为null 109 | if (la == null) return lb; 110 | if (lb == null) return la; 111 | //分别将两个链表赋值给 p、q 112 | Node p = la; 113 | Node q = lb; 114 | //定义一个头结点 115 | Node head; 116 | //比较两个结点数据域中元素的大小 117 | if (p.data < q.data) { 118 | //如果 p 数据小于 q 数据,将 p 结点赋值给head 119 | head = p; 120 | //数据小的那条链表的指针向后移动一位 121 | p = p.next; 122 | } else { 123 | //否则,移动另一个指针 124 | head = q; 125 | q = q.next; 126 | } 127 | //将 head 结点赋值给 r 128 | Node r = head; 129 | //判断两个链表指针指到最后一个结点,要想跳出循环,肯定p、q其中有一个先遍历完 130 | while (p != null && q != null) { 131 | //如果没有指到最后的结点,继续进行比较大小 132 | if (p.data < q.data) { 133 | //r 继承着 head 继续向下添加结点 134 | r.next = p; 135 | p = p.next; 136 | } else { 137 | r.next = q; 138 | q = q.next; 139 | } 140 | //r 指针向下移动,指向新添加的结点 141 | r = r.next; 142 | } 143 | //判断 p、q 两个链表哪一个先遍历晚,最后将剩余的链表拼接到合成链表的最后 144 | if (p != null) { 145 | r.next = p; 146 | } else { 147 | r.next = q; 148 | } 149 | //返回头结点 150 | return head; 151 | } 152 | 153 | /** 154 | * 功能:删除倒数第K个结点 155 | * 思路:我们想需要定义一个头指针,从链表的头部向后移动 k个单位,然后用头指针(fast)标记,在单链表的头部再定义一个指针(slow) 156 | * 之后,fast每向后移动一个结点,solw就紧跟移动一个结点,直到 fast 指针移动到了最后一个结点,slow 指针所指向的结点就是倒数 157 | * 第k个结点,进行单链表的删除即可。 158 | * @param list:要删除的单链表 159 | * @param k:倒数第 k 个 160 | * @return 161 | */ 162 | public static Node deleteLastKth(Node list, int k) { 163 | //将fast指针指向list单链表的开始的第一个结点 164 | Node fast = list; 165 | //i 用来进行计数 166 | int i = 1; 167 | //通过 while 循环,正想找到第 K 个结点(这里有两个判断条件,fast != null 条件的作用是要删除倒数第k个结点 168 | //的单链表不能小于k的长度) 169 | while (fast != null && i < k) { 170 | fast = fast.next; 171 | ++i; 172 | } 173 | //如果单链表的长度小于 k ,就返回 list 单链表 174 | if (fast == null) return list; 175 | 176 | //前边找到第 k 个结点之后,让 slow 指向第一个结点 177 | Node slow = list; 178 | Node prev = null; 179 | //判断fast指针也就是最前边的指针下一个节点是否为 null(如果为null相当于到尾部了) 180 | while (fast.next != null) { 181 | //如果不为 null , fast 指针向后移动一个指针,slow 指针也要移动一个 182 | fast = fast.next; 183 | //prev 指向移动前的结点,为了区别下方单链表的长度和 k 相等 184 | prev = slow; 185 | //slow 向后移动一个 186 | slow = slow.next; 187 | } 188 | //这个判断是,如果单链表的长度正好等于 k ,删除倒数第K个结点也就是删除头结点。 189 | if (prev == null) { 190 | list = list.next; 191 | } else { 192 | //不为上述情况就可以用单链表的删除思想了 193 | prev.next = prev.next.next; 194 | } 195 | //返回已经删除结点的链表 196 | return list; 197 | } 198 | 199 | /** 200 | * 功能:求中间结点 201 | * 思路:定义一个指针 fast 用来移动做逻辑判断(画一下图就很清晰) 202 | * @param list:传入要求中间结点的链表 203 | * @return 204 | */ 205 | public static Node findMiddleNode(Node list) { 206 | //如果单链表为 null,就返回 null 207 | if (list == null) return null; 208 | //slow 指向的就是 fast.next 与 fast.next.next 中间的那个结点 209 | Node fast = list; 210 | Node slow = list; 211 | //循环遍历满足fast指针条件单链表 212 | while (fast.next != null && fast.next.next != null) { 213 | fast = fast.next.next; 214 | slow = slow.next; 215 | } 216 | //返回的slow就是中间结点 217 | return slow; 218 | } 219 | //输入链表的所有值 220 | public static void printAll(Node list) { 221 | Node p = list; 222 | while (p != null) { 223 | System.out.print(p.data + " "); 224 | p = p.next; 225 | } 226 | System.out.println(); 227 | } 228 | } 229 | 230 | ``` 231 | 232 | -------------------------------------------------------------------------------- /Link_List/JAVA/SinglyLinkedList06.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package com.test.xiaolu; 3 | 4 | 5 | /** 6 | * 公众号:「一个不甘平凡的码农」 7 | * @author 小鹿 8 | * 功能: 9 | * [插入] 10 | * 1) 向单链表尾部添加数据 11 | * 2) 头插法 12 | * 3) 插入到指定结点的后边 13 | * 4) 插入到指定结点的前边 14 | * [删除] 15 | * 1) 删除尾部数据 16 | * 2) 删除指定结点 17 | * 3) 删除指定值结点 18 | * [查找] 19 | * 1) 单链表按值查找 20 | * 2) 按照索引查找 21 | * 3) 遍历所有数据 22 | */ 23 | public class SinglyLinkedList { 24 | 25 | public static void main(String[] args) { 26 | SinglyLinkedList s = new SinglyLinkedList(); 27 | s.inserValue(1); 28 | s.inserValue(2); 29 | s.inserValue(3); 30 | System.out.println("按值查找:"+s.findByValue(3)); 31 | System.out.println("按索引查找:"+s.findByIndex(2)); 32 | // s.deleteToTail(); 33 | s.print(); 34 | } 35 | 36 | //定义链表结点 37 | public class Node { 38 | //数据域 39 | int data; 40 | //指针域 41 | Node next; 42 | //初始化 43 | public Node(int data,Node next) { 44 | this.data = data; 45 | this.next = next; 46 | } 47 | } 48 | 49 | //设置头结点为空 50 | private Node head = null; 51 | 52 | /** 53 | * 功能:构建新结点 54 | * @param value 55 | */ 56 | public void inserValue(int value) { 57 | Node newNode = new Node(value, null); 58 | inserToTail(newNode); 59 | } 60 | 61 | /** 62 | * 功能:向单链表尾部添加数据 63 | * 边界条件: 64 | * 1、判断是否是空链表(如果是,则将新结点赋值给 head) 65 | * 66 | * @param value 数据元素 67 | */ 68 | public void inserToTail(Node newNode) { 69 | //判断是否为空链表 70 | if(head == null){ 71 | //将新结点直接赋给 head 72 | head = newNode; 73 | return; 74 | } 75 | Node p = head; 76 | while(p.next != null) { 77 | p = p.next; 78 | } 79 | 80 | p.next = newNode; 81 | } 82 | 83 | 84 | /** 85 | * 时间:2019/2/22 86 | * 功能:头插法 87 | * 边界条件: 88 | * 1、判断链表是否为空链表 89 | * @param newNode 插入的结点 90 | */ 91 | public void createNewNode(int value) { 92 | Node newNode = new Node(value, null); 93 | inserToHead(newNode); 94 | } 95 | public void inserToHead(Node newNode) { 96 | //如果链表是空链表 97 | if(head == null) { 98 | //将新结点作为头部 99 | head = newNode; 100 | }else { 101 | //现将新节点的next指向头结点地址 102 | newNode.next = head; 103 | //将新结点替换成头部 104 | head = newNode; 105 | } 106 | } 107 | 108 | /** 109 | * 时间:2019/2/22 110 | * 功能:将元素插入到指定结点后边 111 | * 边界条件: 112 | * 1、判断指定结点是否为 null 113 | * 114 | * @param p 指定的结点 115 | * @param newNode 插入的结点 116 | */ 117 | public void inserAfter(Node p,int value) { 118 | Node newNode = new Node(value, null); 119 | insertAfter(p,newNode); 120 | } 121 | public void insertAfter(Node p,Node newNode) { 122 | //如果指定的结点为空(如果是空链表,头结点也等于 null),则返回 123 | if(p == null) return; 124 | //将原本 p 结点存储下一结点的地址赋值给新结点的地址域 125 | newNode.next = p.next; 126 | //然后p结点的地址域指向新结点 127 | p.next = newNode; 128 | } 129 | 130 | /** 131 | * 时间:2019/2/22 132 | * 功能:插入结点到指定结点前 133 | * 边界条件: 134 | * 1、判断指定结点 p 是否为 null 135 | * 2、判断指定结点是否为头结点(头插法) 136 | * 3、循环遍历找到指定结点的前一结点信息 137 | * @param p 指定结点 138 | * @param newNode 插入的结点 139 | */ 140 | public void insertBefore(Node p, int value) { 141 | //生成新结点 142 | Node newNode = new Node(value, null); 143 | //插入到指定结点前边 144 | insertBefore(p, newNode); 145 | } 146 | public void insertBefore(Node p,Node newNode) { 147 | //如果指定的结点为空,则返回 148 | if(p == null) return; 149 | //判断是否为头结点 150 | if(head == p) { 151 | inserToHead(newNode); 152 | return; 153 | } 154 | 155 | Node q = head; 156 | //如果 head 为空链表。返回null 157 | if(q == null) return; 158 | //循环遍历找到指向 p 结点的前一个结点 159 | while(q != null && q.next != p) { 160 | q = q.next; 161 | } 162 | //添加结点 163 | newNode.next = q.next; 164 | q.next = newNode; 165 | } 166 | 167 | /** 168 | * 功能:删除尾部数据 169 | * 分析: 170 | * 1、判断是否为空链表 171 | * 2、循环遍历找到最后结点 172 | */ 173 | public void deleteToTail() { 174 | //如果链表为空链表,返回return 175 | if(head == null) return; 176 | Node p = head; 177 | Node q = null; 178 | //循环遍历找到最后结点 179 | while(p.next != null) { 180 | q = p; 181 | p = p.next; 182 | } 183 | //将最后结点的前一结点设置为空 184 | q.next = null; 185 | } 186 | 187 | /** 188 | * 时间:2019/2/23 189 | * 功能:删除指定结点 190 | * 边界条件: 191 | * 1、判断删除结点 p 是否为 null 192 | * 2、判断链表是否为空 193 | * 3、判断删除的结点是否为头结点(重新指定头结点) 194 | * 4、判断是否循环到链尾,是否找到 p 结点 195 | * @param p 指定结点 196 | */ 197 | public void deleteByNode(Node p) { 198 | //如果该结点为空,直接返回 199 | if(p == null) return; 200 | 201 | //如果链表为空,返回 202 | if(head == null) return; 203 | 204 | //如果删除的结点为头结点 205 | if(p == head) { 206 | //设置下一结点为头结点 207 | head = head.next; 208 | } 209 | 210 | //循环遍历找到 p 的前一结点 211 | Node q = head; 212 | //注意:这里是两种终止条件,1、没有找到该结点(q==null) 2、找到了该结点 213 | while(q != null && q.next != p) { 214 | q = q.next; 215 | } 216 | 217 | //如果到链表尾部没有找到 p 结点 218 | if(q == null) { 219 | return; 220 | } 221 | //删除 p 结点 222 | q.next = p.next; 223 | } 224 | 225 | /** 226 | * 时间:2019/2/23 227 | * 功能:删除指定值的结点 228 | * 边界条件: 229 | * 1、判断是否为空链表 230 | * 2、遍历找到该值同时记录该结点的前一个节点 231 | * @param value 删除的值 232 | */ 233 | public void deleteByValue(int value) { 234 | //如果链表为空,则返回 235 | if(head == null) return; 236 | 237 | //循环遍历找到该值 238 | Node p = head; 239 | Node q = null;//用来记录该值结点的前一个节点 240 | while(p != null && p.data != value) { 241 | q = p; 242 | p = p.next; 243 | } 244 | //删除结点 245 | q.next = p.next; 246 | } 247 | 248 | /** 249 | * 时间:2019/2/22 250 | * 功能:单链表按值查找 251 | * @param value 查找的值 252 | * @return 返回该结点 253 | */ 254 | public Node findByValue(int value) { 255 | //将链表头部赋值给 p 256 | Node p = head; 257 | 258 | //循环遍历链表查找该值 259 | while(p != null && p.data != value) { 260 | p = p.next; 261 | } 262 | //判断 p 是否为 null,然后判断 p.data 的值为多少 263 | return p; 264 | } 265 | 266 | /** 267 | * 时间:2019/2/22 268 | * 功能:按照索引查找 269 | * @param index 索引(1 开始) 270 | * @return 返回该结点 271 | */ 272 | public Node findByIndex(int index) { 273 | Node p = head; 274 | //用来记录索引值 275 | int pos = 1; 276 | //循环遍历 277 | while(p != null && pos != index) { 278 | p = p.next; 279 | pos++; 280 | } 281 | //返回 p 结点 282 | return p; 283 | } 284 | 285 | /** 286 | * 功能:打印所有链表结点 287 | */ 288 | public void print() { 289 | Node p = head; 290 | 291 | while(p != null) { 292 | System.out.print(p.data+" "); 293 | p = p.next; 294 | } 295 | } 296 | } 297 | ``` 298 | 299 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 数据结构与算法必会代码实现 2 | 3 | ## 数组 4 | 5 | #### 1、数组实现增、删、改、查 [(Java 实现)](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Array/JAVA/Array.md) 6 | 7 | #### 2、实现一个支持动态扩容的数组 [(Java 实现)](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Array/JAVA/DynamicDilatationArray.md) 8 | 9 | #### 3、实现一个大小固定的有序数组,支持动态增删改操作 ([Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Array/JAVA/FixedOrderArray.md)) 10 | 11 | #### 4、两个有序数组的合并 ([Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Array/JAVA/%E4%B8%A4%E4%B8%AA%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%90%88%E5%B9%B6.md)) 12 | 13 | 14 | 15 | ## 链表 16 | 17 | #### 1、单链表的插入、删除、查找 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/SinglyLinkedList.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/JAVA/SinglyLinkedList06.md)) 18 | 19 | #### 2、双链表的插入、删除 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/DoubleLinkedList.md)) 20 | 21 | #### 3、循环链表的插入、查找、删除 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/CircularLinkedList.md)) 22 | 23 | #### 4、两个有序链表的合并 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/%E4%B8%A4%E4%B8%AA%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8%E7%9A%84%E5%90%88%E5%B9%B6.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/JAVA/LinkedListAlgo07.md)) 24 | 25 | #### 5、删除倒数第 K 个结点 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/%E5%88%A0%E9%99%A4%E5%80%92%E6%95%B0%E7%AC%AC%20K%20%E4%B8%AA%E7%BB%93%E7%82%B9.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/JAVA/LinkedListAlgo07.md)) 26 | 27 | #### 6、反转链表 [(JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/%E5%8F%8D%E8%BD%AC%E9%93%BE%E8%A1%A8.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/JAVA/LinkedListAlgo07.md)) 28 | 29 | #### 7、链表环的检测 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/%E9%93%BE%E8%A1%A8%E7%8E%AF%E7%9A%84%E6%A3%80%E6%B5%8B.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/JAVA/LinkedListAlgo07.md)) 30 | 31 | #### 8、求链表的中间结点 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/javascript/%E6%B1%82%E9%93%BE%E8%A1%A8%E7%9A%84%E4%B8%AD%E9%97%B4%E7%BB%93%E7%82%B9.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Link_List/JAVA/LinkedListAlgo07.md)) 32 | 33 | 34 | 35 | ## 栈 36 | 37 | #### 1、实现一个基于数组的顺序栈([Java 实现]()) 38 | 39 | #### 2、实现一个基于链表的链式栈 ([Java 实现]()) 40 | 41 | 42 | 43 | ## 队列 44 | 45 | #### 1、实现一个基于数组的顺序队列 ([Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Queue/JAVA/ArrayQueue.md)) 46 | 47 | #### 2、实现一个循环队列 ([Java 实现]()) 48 | 49 | 50 | 51 | ## 树 52 | 53 | #### 1、实现二叉树的增、删、查、(前|中|后)遍历 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Tree/JavaScript/%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Tree/JAVA/%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91.md)) 54 | 55 | 56 | 57 | ## 堆 58 | 59 | #### 1、堆的插入与删除 ([Java 实现]()) 60 | 61 | #### 2、堆排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JAVA/sorts.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Heap/JAVA/HeapSort.md)) 62 | 63 | 64 | 65 | ## Trie(字典树) 66 | 67 | #### 1、实现一个字典树 ([JavaScript 实现]() | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Trie/JAVA/Trie%20%E5%AD%97%E5%85%B8%E6%A0%91.md)) 68 | 69 | 70 | 71 | ## 排序 72 | 73 | #### 1、冒泡排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/sorts.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JAVA/sorts.md)) 74 | 75 | #### 2、插入排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/sorts.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JAVA/sorts.md)) 76 | 77 | #### 3、选择排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/sorts.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JAVA/sorts.md)) 78 | 79 | #### 4、希尔排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/ShellSort.md)) 80 | 81 | #### 5、归并排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/MergeSort.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/%E6%B1%82%E7%AC%ACK%E5%A4%A7%E5%85%83%E7%B4%A0.md)) 82 | 83 | #### 6、快速排序 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/QuickSort.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JAVA/QuickSort.md)) 84 | 85 | #### 7、求第 K 大元素 ([JavaScript 实现]([https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/%E6%B1%82%E7%AC%ACK%E5%A4%A7%E5%85%83%E7%B4%A0.md](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Sorts/JavaScript/求第K大元素.md))) 86 | 87 | 88 | 89 | ## 查找 90 | 91 | #### 1、最简单的二分查找 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Find/JavaScript/binarySearch.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Find/Java/BinarySearch.md)) 92 | 93 | #### 2、二分查找的四个扩展 ([JavaScript 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Find/JavaScript/binarySearch.md) | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Find/Java/BinarySearch.md)) 94 | 95 | 96 | 97 | ## 遍历 98 | 99 | #### 1、深度优先遍历 (JavaScript 实现| [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Map/Java/BearthFirstSearch.md)) 100 | 101 | #### 2、广度优先遍历 (JavaScript 实现 | [Java 实现](https://github.com/luxiangqiang/Data-Structure-Coding/blob/master/Map/Java/DepthFirstSearch.md)) 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Stack/JAVA/SampleBrower.md: -------------------------------------------------------------------------------- 1 | ```java 2 | package easy_test; 3 | 4 | /** 5 | * 功能:使用前后栈实现浏览器的前进后退。 6 | * @author :小鹿 7 | * 8 | */ 9 | 10 | public class SampleBrowser { 11 | //存储当前页面变量 12 | private String currentPage; 13 | //backStack 栈(存储浏览器前进时浏览过的页面) 14 | private LinkedListBasedStack backStack; 15 | //forwardStack 栈(存储浏览器后退时浏览过的页面) 16 | private LinkedListBasedStack forwardStack; 17 | 18 | //构造函数 19 | public SampleBrowser() { 20 | //新建一个浏览器栈(backStack) 21 | this.backStack = new LinkedListBasedStack(); 22 | //新建一个浏览器栈(forwardStack) 23 | this.forwardStack = new LinkedListBasedStack(); 24 | } 25 | 26 | //主函数 27 | public static void main(String[] args) { 28 | SampleBrowser browser = new SampleBrowser(); 29 | //打开新网页 30 | browser.open("http://www.baidu.com"); 31 | //打开新网页 32 | browser.open("http://news.baidu.com/"); 33 | //打开新网页 34 | browser.open("http://news.baidu.com/ent"); 35 | //后退网页 36 | browser.goBack(); 37 | //后退网页 38 | browser.goBack(); 39 | //前进页面 40 | browser.goForward(); 41 | //打开新页面 42 | browser.open("http://www.qq.com"); 43 | //前进页面 44 | browser.goForward(); 45 | //后退网页 46 | browser.goBack(); 47 | //前进页面 48 | browser.goForward(); 49 | //后退网页 50 | browser.goBack(); 51 | //后退网页 52 | browser.goBack(); 53 | //后退网页 54 | browser.goBack(); 55 | //后退网页 56 | browser.goBack(); 57 | //查看当前页面 58 | browser.checkCurrentPage(); 59 | } 60 | 61 | //打开一个网页 62 | public void open(String url) { 63 | //判断是不是第一个入栈页面(除第一次调用外,将前一个页面入栈,让当前的页面显示到浏览器) 64 | if (this.currentPage != null) { 65 | //入栈操作 66 | this.backStack.push(this.currentPage); 67 | this.forwardStack.clear(); 68 | } 69 | //第一个入栈网页,显示到界面 70 | showUrl(url, "Open"); 71 | } 72 | 73 | //判断栈大小是否大于0 74 | public boolean canGoBack() { 75 | return this.backStack.size() > 0; 76 | } 77 | 78 | //判断第二个占是否存在页面 79 | public boolean canGoForward() { 80 | return this.forwardStack.size() > 0; 81 | } 82 | 83 | //浏览器退回 84 | public String goBack() { 85 | //如果栈不为空 86 | if (this.canGoBack()) { 87 | //将当前页放到另一个栈中 88 | this.forwardStack.push(this.currentPage); 89 | //原来的栈数据出栈 90 | String backUrl = this.backStack.pop(); 91 | //将出栈的页面显示到浏览器界面上 92 | showUrl(backUrl, "Back"); 93 | //返回该页面 94 | return backUrl; 95 | } 96 | //否则提示不能进行浏览器倒退 97 | System.out.println("* Cannot go back, no pages behind."); 98 | return null; 99 | } 100 | 101 | //浏览器前进 102 | public String goForward() { 103 | //判断第二个栈中是否有页面 104 | if (this.canGoForward()) { 105 | //当前页面入栈 106 | this.backStack.push(this.currentPage); 107 | //前进的页面出栈 108 | String forwardUrl = this.forwardStack.pop(); 109 | //将出栈的页面显示到浏览器中 110 | showUrl(forwardUrl, "Foward"); 111 | //返回当前页面 112 | return forwardUrl; 113 | } 114 | //否则,提示浏览器不能进行前进 115 | System.out.println("** Cannot go forward, no pages ahead."); 116 | return null; 117 | } 118 | 119 | //显示网页,并标记为当前网页 120 | public void showUrl(String url, String prefix) { 121 | this.currentPage = url; 122 | System.out.println(prefix + " page == " + url); 123 | } 124 | 125 | //当前页面是哪一个页面 126 | public void checkCurrentPage() { 127 | System.out.println("Current page is: " + this.currentPage); 128 | } 129 | 130 | /** 131 | * A LinkedList based Stack implementation. 132 | */ 133 | public static class LinkedListBasedStack { 134 | 135 | // 测试栈代码 136 | // public static void main(String[] args) { 137 | // LinkedListBasedStack stack = new LinkedListBasedStack(); 138 | // stack.push("A"); 139 | // stack.push("B"); 140 | // stack.push("C"); 141 | // stack.pop(); 142 | // stack.push("D"); 143 | // stack.push("E"); 144 | // stack.pop(); 145 | // stack.push("F"); 146 | // stack.print(); 147 | // 148 | //// String data = stack.getTopData(); 149 | //// System.out.println("Top data == " + data); 150 | // } 151 | 152 | //栈的大小 153 | private int size; 154 | //栈顶部 155 | private Node top; 156 | 157 | //该定义一个结点类 158 | public static class Node { 159 | 160 | //数据域 161 | private String data; 162 | //指针域 163 | private Node next; 164 | 165 | public Node(String data) { 166 | this(data, null); 167 | } 168 | 169 | public Node(String data, Node next) { 170 | this.data = data; 171 | this.next = next; 172 | } 173 | 174 | public void setData(String data) { 175 | this.data = data; 176 | } 177 | 178 | public String getData() { 179 | return this.data; 180 | } 181 | 182 | public void setNext(Node next) { 183 | this.next = next; 184 | } 185 | 186 | public Node getNext() { 187 | return this.next; 188 | } 189 | } 190 | 191 | //创建一个结点 192 | static Node createNode(String data, Node next) { 193 | return new Node(data, next); 194 | } 195 | //清空栈 196 | public void clear() { 197 | this.top = null; 198 | this.size = 0; 199 | } 200 | //入栈 201 | public void push(String data) { 202 | Node node = createNode(data, this.top); 203 | this.top = node; 204 | this.size++; 205 | } 206 | //出栈 207 | public String pop() { 208 | Node popNode = this.top; 209 | if (popNode == null) { 210 | System.out.println("Stack is empty."); 211 | return null; 212 | } 213 | this.top = popNode.next; 214 | if (this.size > 0) { 215 | this.size--; 216 | } 217 | return popNode.data; 218 | } 219 | //获取栈顶元素 220 | public String getTopData() { 221 | if (this.top == null) { 222 | return null; 223 | } 224 | return this.top.data; 225 | } 226 | //栈的大小 227 | public int size() { 228 | return this.size; 229 | } 230 | //输出栈中的所有元素 231 | public void print() { 232 | System.out.println("Print stack:"); 233 | Node currentNode = this.top; 234 | while (currentNode != null) { 235 | String data = currentNode.getData(); 236 | System.out.print(data + "\t"); 237 | currentNode = currentNode.next; 238 | } 239 | System.out.println(); 240 | } 241 | } 242 | } 243 | 244 | ``` 245 | 246 | -------------------------------------------------------------------------------- /Tree/JavaScript/二叉查找树.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /** 3 | * 时间:2019/3/29 4 | * 公众号: 「一个不甘平凡的码农」 5 | * 二叉查找树的增、删、改、查 6 | * 功能: 7 | * 1)插入数据 8 | * 2)查找数据 9 | * 3)删除数据 10 | * 4)查找最大值 11 | * 5)查找最小值 12 | * 6)前、中、后序遍历 13 | * @author 小鹿 14 | * 15 | */ 16 | 17 | //定义树节点 18 | class Node{ 19 | constructor(data){ 20 | this.data = data; 21 | this.left = null; 22 | this.right = null; 23 | } 24 | 25 | //插入数据 26 | //步骤: 27 | // 1、判断是否为空树(是:将数据加入到第一个结点) 28 | // 2、循环根节点不等于 null 29 | // 3、判断与根节点的大小 30 | // 4、判断左/右子树是否为 null 31 | // 5、递归左子树/递归右子树 32 | insertValue = (tree,val) =>{ 33 | //封装节点 34 | let newNode = new Node(val); 35 | 36 | //判断是否为空树 37 | if(tree === null) { 38 | tree = newNode; 39 | return true; 40 | }else{ 41 | while(tree != null){ 42 | //判断与根节点的大小 43 | if(newNode.data < tree.data){ 44 | //判断左子树是否为null 45 | if(tree.left == null){ 46 | tree.left = newNode; 47 | return true; 48 | }else{ 49 | tree = tree.left; 50 | } 51 | }else{ 52 | //val 大于根节点 53 | if(tree.right == null){ 54 | tree.right = newNode; 55 | return true; 56 | }else{ 57 | tree = tree.right; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | 65 | //查找数据 66 | //步骤: 67 | // 1、判断树是否为 null 68 | // 2、判断查找数据于根节点大小 69 | // 3、递归左/右子树 70 | findData = (tree,val) =>{ 71 | //判断树是否为 null 72 | if(tree == null){ 73 | return false; 74 | } 75 | 76 | while(tree !== null){ 77 | //判断查找值与根节点的大小 78 | if(val == tree.data){ 79 | return tree.data; 80 | }else if(val < tree.data){ 81 | if(tree.left == null){ 82 | return false; 83 | }else{ 84 | tree = tree.left; 85 | } 86 | }else{ 87 | if(tree.right == null){ 88 | return false; 89 | }else{ 90 | tree = tree.right; 91 | } 92 | } 93 | } 94 | } 95 | 96 | //删除数据 97 | //步骤: 98 | // 1、定义两个节点(p用来存放删除节点,pp用来存放删除节点的父节点) 99 | // 2、遍历树查找要删除的节点(条件:找不到该值/找到了该值) 100 | // 2、判断是否找到该值 101 | // 3、判断要删除的树节点子节点个数(无子节点、一个子节点、两个子节点) 102 | // 1)情况一(节点的两个子节点都不等于null):两个子节点:在右子树先寻找最小值(同时记录最小值的父节点),做数值交换, 103 | // 然后让删除节点的p指针指向最小值,同时让删除节点的父节点指向删除值的父节点。转化为删除叶子节点问题。 104 | // 2)情况二(节点的其中一个结点为 null):用 child 存储删除节点的子节点 105 | // 3)情况三(删除叶子节点):让 child 存储 null 106 | // 4、对标记好的节点进行删除 107 | // 5、判断树中只剩下根节点 108 | // 6、判断父节点下哪个子节点删除了,让父节点直接指向 child 109 | deleteData = (p,val) =>{ 110 | //定义父节点(记录删除节点的父节点) 111 | let pp = null; 112 | 113 | //寻找该删除节点 114 | while(p !== null && p.data != val){ 115 | //记录删除节点的父节点 116 | pp = p; 117 | //判断删除值与根节点大小关系 118 | if(val > p.data){ 119 | p = p.right; 120 | }else{ 121 | p = p.left; 122 | } 123 | } 124 | 125 | // 如果树为 null 或者找不到该删除节点 126 | if(p == null){ 127 | return false; 128 | } 129 | 130 | //情况一:该删除节点有两个子节点 131 | if(p.left != null && p.right != null ){ 132 | //寻找右子树最小节点(默认右子树最小节点) 133 | let minP = p.right; 134 | //记录最小值的父节点 135 | let minPP = p; 136 | //判断右子节点是否有左子树 137 | while(minP.left != null){ 138 | //记录最小节点的父节点 139 | minPP = minPP; 140 | //找到最小节点 141 | minP = minP.left; 142 | } 143 | //节点值交换 144 | p.data = minP.data; 145 | //最小值节点交换,随之指向删除节点的指针和父节点的指针变换 146 | p = minP; 147 | //父节点交换 148 | pp = minPP; 149 | } 150 | 151 | //如果为情况一,经上述变换,成为了删除一个叶子节点(情况三) 152 | //情况二:删除节点是叶子节点或仅有一个节点 153 | let child = null; 154 | if(p.left != null){ 155 | child = p.left; 156 | }else if(p.right != null){ 157 | child = p.right; 158 | }else{ 159 | //情况三:删除节点为叶子节点 160 | child = null; 161 | } 162 | 163 | //对删除节点进行删除操作 164 | if(pp == null){ 165 | //树的根节点为 null(树中只有一个节点) 166 | this.tree = child; 167 | }else if(pp.left == p){ 168 | //该删除节点在父节点的左边的情况 169 | pp.left = child; 170 | }else{ 171 | //该删除节点在父节点的右边情况 172 | pp.right = child; 173 | } 174 | } 175 | } 176 | 177 | //求树中最大节点 178 | treeMaxNode = (tree)=>{ 179 | // 180 | if(tree === null) return false; 181 | // 182 | while(tree.right != null){ 183 | tree = tree.right; 184 | } 185 | return tree.data; 186 | } 187 | 188 | //求树中最小节点 189 | treeMinNode = (tree)=>{ 190 | // 191 | if(tree === null) return false; 192 | // 193 | while(tree.left != null){ 194 | tree = tree.left; 195 | } 196 | return tree.data; 197 | } 198 | 199 | //遍历二叉查找树 200 | //前序遍历 201 | preorderTraversal = (tree) =>{ 202 | //判断树是否为空 203 | if(tree == null) return false; 204 | //根节点 205 | console.log(tree.data) 206 | //左子树 207 | this.preorderTraversal(tree.left) 208 | //右子树 209 | this.preorderTraversal(tree.right) 210 | } 211 | 212 | //中序遍历 213 | inorderTraversal = (tree) =>{ 214 | //判断树是否为空 215 | if(tree == null) return false; 216 | //左子树 217 | this.inorderTraversal(tree.left); 218 | //根节点 219 | console.log(tree.data) 220 | //右节点 221 | this.inorderTraversal(tree.right); 222 | } 223 | 224 | //后序遍历 225 | postorderTraversal = (tree) =>{ 226 | //判断树是否为空 227 | if(tree == null) return false; 228 | //左子树 229 | this.postorderTraversal(tree.left); 230 | //右子树 231 | this.postorderTraversal(tree.right); 232 | //根节点 233 | console.log(tree.data) 234 | } 235 | 236 | //测试 237 | const tree = new Node(1); 238 | console.log('------------------------------插入数据--------------------------') 239 | tree.insertValue(tree,12) 240 | tree.insertValue(tree,9) 241 | tree.insertValue(tree,24) 242 | tree.insertValue(tree,8) 243 | tree.insertValue(tree,10) 244 | tree.insertValue(tree,13) 245 | tree.insertValue(tree,30) 246 | tree.insertValue(tree,7) 247 | inorderTraversal(tree); 248 | console.log('-----------------------------查找数据--------------------------') 249 | console.log(tree.findData(tree,30)) 250 | console.log('-----------------------------删除数据--------------------------') 251 | console.log('--------------------------删除只有一个节点----------------------') 252 | tree.deleteData(tree,8) 253 | inorderTraversal(tree); 254 | console.log('----------------------------删除叶子节点-----------------------') 255 | tree.deleteData(tree,30) 256 | inorderTraversal(tree); 257 | console.log('---------------------------删除有两个节点----------------------') 258 | tree.deleteData(tree,9) 259 | inorderTraversal(tree); 260 | console.log('------------------------------前序遍历-------------------------') 261 | preorderTraversal(tree); 262 | console.log('------------------------------中序遍历-------------------------') 263 | inorderTraversal(tree); 264 | console.log('------------------------------后序遍历-------------------------') 265 | postorderTraversal(tree); 266 | console.log('------------------------------求最大值-------------------------') 267 | console.log(treeMaxNode(tree)) 268 | console.log('------------------------------求最小值-------------------------') 269 | console.log(treeMinNode(tree)) 270 | ``` 271 | 272 | --------------------------------------------------------------------------------