diff --git a/.gitignore b/.gitignore index 31f5f1e9..ebfdc5cb 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,34 @@ *.zip *.tar.gz *.rar +*.DS_Store +*.exe # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # editor files .vscode +.*.swp + +# WebStorm +.idea/ + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ + +**/*.idea +**/*.iml +**/*out diff --git a/DynamicStackBaseArray.java b/DynamicStackBaseArray.java new file mode 100644 index 00000000..d04bc389 --- /dev/null +++ b/DynamicStackBaseArray.java @@ -0,0 +1,136 @@ +package Stack; + +import java.util.Iterator; + +/** + * 顺序栈的动态扩容 + * Author: PeiJiaNi + * @param 顺序栈元素类型 + */ + +public class DynamicStackBaseArray implements Iterable { + private T[] items; // 数组 + private int count; // 栈中的元素个数 + private int length; // 栈空间大小 + + /** + * 初始化栈 + * + * @param length 栈空间大小 + */ + public DynamicStackBaseArray(int length) { + this.items = (T[]) new Object[length]; + this.count = 0; + this.length = length; + } + + /** + * 入栈操作 平均时间复杂度O(1) + * + * @param item 入栈元素 + */ + public void push(T item) { + // 栈空间已满,则扩容 + if (count == length) { + resize(2 * items.length); + } + + items[count++] = item; + } + + /** + * 出栈操作 平均时间复杂度O(1) + * + * @return 如果栈内不为空,则返回栈顶元素,否则返回-1 + */ + public T pop() { + if (count == 0) { + System.out.println("当前栈已空,无法进行出栈操作"); + return null; + } + + T item = items[--count]; + items[count] = null; + + if (count > 0 && (count == items.length / 4)) { + resize(items.length / 2); + } + + // 返回下标为 count-1 的数组元素,并且栈中元素个数count-1 + return item; + } + + /** + * 栈空间动态增加或减小 + * + * @param size + */ + private void resize(int size) { + T[] newItems = (T[]) new Object[size]; + for (int i = 0; i < count; i++) { + newItems[i] = this.items[i]; + } + this.items = newItems; + } + + //返回栈中最近添加的元素而不删除它 + public T peek() { + return items[count - 1]; + } + + /** + * 判断当前栈是否为空 + * + * @return 栈为空,则返回true,否则返回-1 + */ + public boolean isEmpty() { + return count == 0; + } + + /** + * 返回栈中元素个数 + * + * @return + */ + public int size() { + return count; + } + + @Override + public Iterator iterator() { + return new ArrayIterator(); + } + + // 内部类 + class ArrayIterator implements Iterator { + int numOfItems = count; + + @Override + public boolean hasNext() { + return numOfItems > 0; + } + + @Override + public T next() { + return items[--numOfItems]; + } + } + + public static void main(String[] args) { + DynamicStackBaseArray stack = new DynamicStackBaseArray(6); + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + stack.push(5); + // System.out.println(stack.peek()); + Iterator iterator = stack.iterator(); + // System.out.println(iterator.hasNext()); + while (iterator.hasNext()) { + System.out.println(iterator.next()); + } + + } + +} + diff --git a/README.md b/README.md index a915e840..ceae59ef 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,77 @@ -# 数据结构和算法之美 -# 请点击查看:[https://time.geekbang.org/column/intro/126](https://time.geekbang.org/column/intro/126) +# 数据结构和算法必知必会的50个代码实现 +### 微信搜索我的公众号“小争哥”,或者微信扫描下面二维码, 获取更多压箱底的干货分享 +### 前Google工程师,5万+人跟着学的《数据结构和算法之美》专栏作者 +![t2](https://github.com/wangzheng0822/markdownphotos/blob/master/pics/qrcode_for_gh_9b0e7afdff20_258.jpg) + +## 数组 +* 实现一个支持动态扩容的数组 +* 实现一个大小固定的有序数组,支持动态增删改操作 +* 实现两个有序数组合并为一个有序数组 + +## 链表 +* 实现单链表、循环链表、双向链表,支持增删操作 +* 实现单链表反转 +* 实现两个有序的链表合并为一个有序链表 +* 实现求链表的中间结点 + +## 栈 +* 用数组实现一个顺序栈 +* 用链表实现一个链式栈 +* 编程模拟实现一个浏览器的前进、后退功能 + +## 队列 +* 用数组实现一个顺序队列 +* 用链表实现一个链式队列 +* 实现一个循环队列 + +## 递归 +* 编程实现斐波那契数列求值f(n)=f(n-1)+f(n-2) +* 编程实现求阶乘n! +* 编程实现一组数据集合的全排列 + +## 排序 +* 实现归并排序、快速排序、插入排序、冒泡排序、选择排序 +* 编程实现O(n)时间复杂度内找到一组数据的第K大元素 + +## 二分查找 +* 实现一个有序数组的二分查找算法 +* 实现模糊二分查找算法(比如大于等于给定值的第一个元素) + +## 散列表 +* 实现一个基于链表法解决冲突问题的散列表 +* 实现一个LRU缓存淘汰算法 + +## 字符串 +* 实现一个字符集,只包含a~z这26个英文字母的Trie树 +* 实现朴素的字符串匹配算法 + +## 二叉树 +* 实现一个二叉查找树,并且支持插入、删除、查找操作 +* 实现查找二叉查找树中某个节点的后继、前驱节点 +* 实现二叉树前、中、后序以及按层遍历 + +## 堆 +* 实现一个小顶堆、大顶堆、优先级队列 +* 实现堆排序 +* 利用优先级队列合并K个有序数组 +* 求一组动态数据集合的最大Top K + +## 图 +* 实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法 +* 实现图的深度优先搜索、广度优先搜索 +* 实现Dijkstra算法、A*算法 +* 实现拓扑排序的Kahn算法、DFS算法 + +## 回溯 +* 利用回溯算法求解八皇后问题 +* 利用回溯算法求解0-1背包问题 + +## 分治 +* 利用分治算法求一组数据的逆序对个数 + +## 动态规划 +* 0-1背包问题 +* 最小路径和 +* 编程实现莱文斯坦最短编辑距离 +* 编程实现查找两个字符串的最长公共子序列 +* 编程实现一个数据序列的最长递增子序列 diff --git a/StackBaseArray.java b/StackBaseArray.java new file mode 100644 index 00000000..dc1ee18b --- /dev/null +++ b/StackBaseArray.java @@ -0,0 +1,64 @@ +package Stack; + +/** + * 顺序栈(基于数组实现) + * Author: PeiJiaNi + */ +public class StackBaseArray { + private int[] items; // 数组 + private int count; // 栈中元素个数 + private int length; // 栈空间大小 + + public StackBaseArray(int capactiy) { + this.items = new int[capactiy]; + this.count = 0; + this.length = capactiy; + } + + /** + * 入栈操作 时间复杂度O(1) + * @param item 要入栈的元素 + * @return 入栈成功则返回true,否则返回false + */ + public boolean push(int item) { + if(count == length) { + System.out.println("当前栈已满,无法进行入栈操作"); + return false; + } + items[count] = item; + ++count; + return true; + } + + /** + * 出栈操作 时间复杂度O(1) + * @return 如果栈内不为空,则返回栈顶元素,否则返回-1 + */ + public int pop(){ + if(count == 0) { + System.out.println("当前栈已空,无法进行出栈操作"); + return -1; + } + + // 返回下标为 count-1 的数组元素,并且栈中元素个数count-1 + return items[--count]; + } + + public static void main(String[] args){ + StackBaseArray stack = new StackBaseArray(6); + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + stack.push(5); + System.out.println(stack.pop()); + System.out.println(stack.pop()); + System.out.println(stack.pop()); + System.out.println(stack.pop()); + System.out.println(stack.pop()); + + } + + +} + diff --git a/c-cpp/05_array/Array_gp.c b/c-cpp/05_array/Array_gp.c new file mode 100644 index 00000000..0147a121 --- /dev/null +++ b/c-cpp/05_array/Array_gp.c @@ -0,0 +1,278 @@ +#include "Array.h" + +#include +#include + +Array* arrayCreate() +{ + struct Array *array = NULL; + array = malloc(sizeof(*array)); + if (NULL == array) + { + return NULL; + } + + array->p = NULL; + + array->size = 0; + array->typeSize = 0; + array->len = 0; + + array->dup = NULL; + array->free = NULL; + array->match = NULL; + + return array; +} + +void arrayInit(Array *array, int size, int typeSize) +{ + if (NULL == array + || typeSize <= 0 + || size < 0) + { + return; + } + + void *p = calloc(1, size* typeSize); + if (NULL == p) + { + return; + } + + array->p = p; + array->len = 0; + array->size = size; + array->typeSize = typeSize; +} + +int arrayInsert(Array *array, size_t pos, void *const value) +{ + if (NULL == array) + { + return -1; + } + + if (array->len >= array->size) + { + return -2; + } + + if (pos > array->size || pos <= 0) + { + return -3; + } + + char *pBegin = array->p; + for (size_t i = array->len; i > pos - 1; --i) + { + void *pNew = pBegin + i * array->typeSize; + void *pOld = pBegin + (i - 1) *array->typeSize; + if (NULL != array->dup) + { + array->dup(pNew, pOld); + } + else + { + memcpy(pNew, pOld, array->typeSize); + } + } + + void *pCopy = (void*)(pBegin + ((pos - 1) * array->typeSize)); + if (NULL != array->dup) + { + array->dup(pCopy, value); + } + else + { + memcpy(pCopy, value, array->typeSize); + } + ++array->len; + return 0; +} + +size_t arraySearchValue(Array *array, void* const value) +{ + if (NULL == array) + { + return -1; + } + + char *pBegin = array->p; + size_t i = 0; + for (; i < array->len; ++i) + { + int nCmp = 0; + if (NULL != array->match) + { + nCmp = array->match(pBegin + i * array->typeSize, value); + } + else + { + nCmp = memcmp(pBegin + i * array->typeSize, value, array->typeSize); + } + + if (nCmp == 0) + { + break; + } + } + + return i; +} + +void* arrayIndex(Array *array, size_t index) +{ + if (NULL == array) + { + return NULL; + } + + if (index > array->len + || index <= 0) + { + return NULL; + } + + char *pBegin = array->p; + return pBegin + array->typeSize * (index - 1); +} + +int arrayModify(Array *array, size_t pos, void *const value) +{ + if (NULL == array) + { + return -1; + } + if (pos > array->len + || pos <= 0) + { + return -2; + } + + char *pBegin = array->p; + void *pOld = pBegin + (pos - 1) * array->typeSize; + if (NULL != array->dup) + { + array->dup(pOld, value); + } + else + { + memcpy(pOld, value, array->typeSize); + } + + return 0; +} + +size_t arrayLen(Array *array) +{ + if (NULL == array) + { + return 0; + } + + return array->len; +} + +size_t arraySize(Array *array) +{ + if (NULL == array) + { + return 0; + } + + return array->size; +} + +void arrayEmpty(Array *array) +{ + if (NULL == array) + { + return; + } + + free(array->p); + array->p = NULL; + free(array); + array = NULL; +} + +void arrayDelValue(Array *array, void *value) +{ + if (NULL == array) + { + return; + } + + char* pBegin = array->p; + bool bCopy = false; + for (size_t i = 0; i < array->len; ++i) + { + if (!bCopy) + { + int nCmp = 0; + if (NULL != array->match) + { + nCmp = array->match(pBegin + i * array->typeSize, value); + } + else + { + nCmp = memcmp(pBegin + i * array->typeSize, value, array->typeSize); + } + + if (0 == nCmp) + { + bCopy = true; + continue; + } + } + else + { + void *pOld = pBegin + (i + 1) * array->typeSize; + void *pNew = pBegin + i * array->typeSize; + if (NULL != array->dup) + { + array->dup(pNew, pOld); + } + else + { + memcpy(pNew, pOld, array->typeSize); + } + } + } + + if (bCopy) + { + --array->len; + } +} + +void arrayDelIndex(Array *array, size_t pos) +{ + if (NULL == array) + { + return; + } + + if (pos > array->len || pos <= 0) + { + return; + } + + char* pBegin = array->p; + for (size_t i = pos - 1; i < array->len - 1; ++i) + { + void *pOld = pBegin + (i + 1) * array->typeSize; + void *pNew = pBegin + i * array->typeSize; + if (NULL != array->dup) + { + array->dup(pNew, pOld); + } + else + { + memcpy(pNew, pOld, array->typeSize); + } + } + + --array->len; +} \ No newline at end of file diff --git a/c-cpp/05_array/Array_gp.h b/c-cpp/05_array/Array_gp.h new file mode 100644 index 00000000..662ef8a5 --- /dev/null +++ b/c-cpp/05_array/Array_gp.h @@ -0,0 +1,48 @@ +#ifndef __ARRAY_H__ +#define __ARRAY_H__ + +#include +#include + +typedef struct Array +{ + // pָĿռС + size_t size; + // pָѾʹõĿռС + size_t len; + // ͵ĴС + size_t typeSize; + // ֵƺ + void(*dup)(void *ptr, void *key); + // ֵͷź + void(*free)(void *ptr); + // ֵȽϺ + int(*match)(void *ptr, void *key); + // ݵָ + void *p; +}Array; + +#define arraySetDupMethod(a, m) ((a)->dup = (m)) +#define arraySetFreeMethod(a, m) ((a)->free = (m)) +#define arraySetMatchMethod(a, m) ((a)->match = (m)) + +#define arrayGetDupMethod(a) ((a)->dup) +#define arrayGetFree(a) ((a)->free) +#define arrayGetMatchMethod(a) ((a)->match) + +Array* arrayCreate(); +void arrayInit(Array *array, int size, int typeSize); + +int arrayInsert(Array *array, size_t pos, void *const value); +size_t arraySearchValue(Array *array, void* const value); +void* arrayIndex(Array *array, size_t index); +int arrayModify(Array *array, size_t pos, void *const value); + +size_t arrayLen(Array *array); +size_t arraySize(Array *array); + +void arrayEmpty(Array *array); +void arrayDelValue(Array *array, void *value); +void arrayDelIndex(Array *array, size_t pos); + +#endif // !__ARRAY_H__ \ No newline at end of file diff --git a/c-cpp/05_array/array.c b/c-cpp/05_array/array.c index 86d9b667..51f6c452 100644 --- a/c-cpp/05_array/array.c +++ b/c-cpp/05_array/array.c @@ -33,7 +33,7 @@ int insert(struct array *array, int elem) } if (idx < array->used) - memmove(&array->arr[array->used], &array->arr[idx], + memmove(&array->arr[idx+1], &array->arr[idx], (array->used - idx) * sizeof(int)); array->arr[idx] = elem; @@ -47,7 +47,7 @@ int delete(struct array *array, int idx) return -1; memmove(&array->arr[idx], &array->arr[idx+1], - (array->used - idx) * sizeof(int)); + (array->used - idx - 1) * sizeof(int)); array->used--; return 0; } diff --git a/c-cpp/06_linkedlist/Dlist/Dlist.c b/c-cpp/06_linkedlist/Dlist/Dlist.c new file mode 100644 index 00000000..8dae9291 --- /dev/null +++ b/c-cpp/06_linkedlist/Dlist/Dlist.c @@ -0,0 +1,220 @@ +/************************************************************************* + > File Name: Dlist.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-07 + > Desc: + ************************************************************************/ +#include +#include +#include "./Dlist.h" + + + +void dlist_init(stDlistHead *dlist) //链表初始化 +{ + dlist->size = 0; + dlist->head = NULL; + dlist->tail = NULL; + return; +} + +void dlist_destory(stDlistHead *dlist) //删除链表 +{ + stDlistNode *pNode = NULL; + + while(dlist->size > 0) + { + pNode = dlist->head; + dlist->head = dlist->head->next; + free(pNode); + dlist->size--; + } + + memset(dlist,0,sizeof(stDlistHead)); + + return; +} + +int dlist_insert_head(stDlistHead *dlist,stDlistNode *pNode,int data) //插入头结点,操作的链表,操作的节点,数据 +{ + if(pNode == NULL) //当只传递一个数据时 + { + pNode = (stDlistNode *)malloc(sizeof(stDlistNode)); //新建节点,为节点分配空间(malloc()可能需要#include) + if (pNode == NULL) + { + return -1; + } + } + + pNode->data = data; + pNode->prev = NULL; + pNode->next = NULL; + + if (dlist->size == 0) //如果链表长度为0,即链表当前无节点, + { + dlist->head = pNode; + dlist->tail = pNode; + } + else //如果链表已有节点,则令新插入节点为头节点 + { + pNode->next = dlist->head; + dlist->head->prev = pNode; + dlist->head = pNode; + } + + dlist->size++; //每成功调用一次,链表长度+1 + return 0; +} + +stDlistNode * dlist_remove_tail(stDlistHead *dlist) //删除尾部节点,并返回删除节点 +{ + stDlistNode *pNode = NULL; + + if(dlist->size == 0) + { + return NULL; + } + + pNode = dlist->tail; + if(dlist->size > 1) + { + dlist->tail = dlist->tail->prev; + dlist->tail->next = NULL; + } + else + { + dlist->head = NULL; + dlist->tail = NULL; + } + dlist->size--; + return pNode; +} + +void dlist_remove_node(stDlistHead * dlist,stDlistNode *pNode) //删除指定节点 +{ + if ((dlist == NULL)||(pNode == NULL)) + { + return; + } + + if (dlist->head == pNode) + { + dlist->head = dlist->head->next; + } + else if (dlist->tail == pNode) + { + dlist->tail = pNode->prev; + + dlist->tail->next = NULL; + } + else + { + pNode->prev->next = pNode->next; + pNode->next->prev = pNode->prev; + } + dlist->size--; + pNode->prev = NULL; + pNode->next = NULL; + + if (dlist->size == 0) + { + memset(dlist,0,sizeof(stDlistHead)); //将dlist占用内存块的所有值置为0,也就是清空head,tail指针内容 + } + + return; +} +stDlistNode * dlist_search(stDlistHead * dlist,int data) //根据值搜索节点,并返回 +{ + stDlistNode *pNode = dlist->head; + while(pNode != NULL) + { + if (pNode->data == data) + { + return pNode; + } + pNode = pNode->next; + + } + return NULL; +} + +void dlist_dump(stDlistHead *dlist) //显示链表中的数据 +{ + int no = 0; + stDlistNode *pNode = dlist->head; + while(pNode != NULL) + { + printf("\r\n [%d] = %d",no++,pNode->data); + pNode = pNode->next; //将pNode的下一个节点赋值给pNode,推进循环 + } + + return; +} + + +void Lru_dlist(stDlistHead *dlist,int data) //LRU(最近最少使用)缓存淘汰算法 +{ + stDlistNode *pNode = NULL; + + pNode = dlist_search(dlist,data); + if (pNode != NULL) //如果在链表中找到这个值,则删除储存这个值的节点,之后吧这个节点放在头部 + { + dlist_remove_node(dlist,pNode); + } + else if(dlist->size >= 4) //没在链表中找到,且链表长度大于4,则从链表中删除尾部节点,将新数据放在头部 + { + pNode = dlist_remove_tail(dlist); + + } + + dlist_insert_head(dlist ,pNode,data); + + return; +} + +int main() +{ + stDlistHead dlist = {0}; + stDlistNode * pNode = NULL; + + dlist_init(&dlist); + + printf("\r\n inset 1,2,3"); + dlist_insert_head(&dlist,NULL,1); + dlist_insert_head(&dlist,NULL,2); + dlist_insert_head(&dlist,NULL,3); + + dlist_dump(&dlist); + + pNode = dlist_remove_tail(&dlist); + if(pNode != NULL) + { + printf("\r\n remove %d",pNode->data); + } + dlist_insert_head(&dlist,pNode,4); + dlist_dump(&dlist); + + Lru_dlist(&dlist,5); + dlist_dump(&dlist); + Lru_dlist(&dlist,6); + dlist_dump(&dlist); + Lru_dlist(&dlist,7); + dlist_dump(&dlist); + Lru_dlist(&dlist,5); + dlist_dump(&dlist); + + + + while(dlist.size > 0) + { + pNode = dlist_remove_tail(&dlist); + if(pNode != NULL) + { + printf("\r\n remove %d",pNode->data); + free (pNode); + } + } + + return 0; +} diff --git a/c-cpp/06_linkedlist/Dlist/Dlist.h b/c-cpp/06_linkedlist/Dlist/Dlist.h new file mode 100644 index 00000000..4b633540 --- /dev/null +++ b/c-cpp/06_linkedlist/Dlist/Dlist.h @@ -0,0 +1,23 @@ +/************************************************************************* + > File Name: Dlist.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-07 + > Desc: + ************************************************************************/ +#include + +typedef struct DlistNode //双向链表中每一个节点 +{ + struct DlistNode *prev; //节点前项指针 + struct DlistNode *next; //节点后项指针 + int data; //数据 +}stDlistNode; + +typedef struct Dlisthead //定义链表总体 +{ + int size; //链表长度 + stDlistNode *head; //头指针 + stDlistNode *tail; //尾部指针 +}stDlistHead; + diff --git a/c-cpp/06_linkedlist/palindromeList/LinkedList.hpp b/c-cpp/06_linkedlist/palindromeList/LinkedList.hpp index 737e41e8..b7f4f908 100644 --- a/c-cpp/06_linkedlist/palindromeList/LinkedList.hpp +++ b/c-cpp/06_linkedlist/palindromeList/LinkedList.hpp @@ -58,7 +58,7 @@ LinkedList::~LinkedList() { ListNode* LinkedList::FindElem(int elemVal) { ListNode *p; - for (p = this -> head; p != nullptr; p = p -> next) { + for (p = this -> head -> next; p != nullptr; p = p -> next) { if (p -> val == elemVal) { return p; } diff --git a/c-cpp/06_linkedlist/single_list.c b/c-cpp/06_linkedlist/single_list.c index 6c12868e..a267595e 100644 --- a/c-cpp/06_linkedlist/single_list.c +++ b/c-cpp/06_linkedlist/single_list.c @@ -47,7 +47,8 @@ struct single_list* del(struct single_list **prev) if (!prev) return NULL; - + if (*prev == null) + return NULL; tmp = *prev; *prev = (*prev)->next; tmp->next = NULL; @@ -74,7 +75,7 @@ struct single_list** search(struct single_list_head* head, int target) void reverse(struct single_list_head* head) { - struct single_list_head tmp; + struct single_list_head tmp = {NULL}; struct single_list *elem; while (!is_empty(head)) { diff --git a/c-cpp/06_linkedlist/singlelist_gc/singleList.c b/c-cpp/06_linkedlist/singlelist_gc/singleList.c new file mode 100644 index 00000000..e7c00a93 --- /dev/null +++ b/c-cpp/06_linkedlist/singlelist_gc/singleList.c @@ -0,0 +1,281 @@ +#include "singleList.h" + +#include + +linkedList * listCreate() +{ + linkedList *list = NULL; + list = malloc(sizeof(*list)); + if (NULL == list) + { + return NULL; + } + + list->dup = NULL; + list->free = NULL; + list->match = NULL; + + list->head = NULL; + list->len = 0; + + return list; +} + +// ͷ +void listRelease(linkedList *list) +{ + if (NULL == list) + { + return; + } + + listEmpty(list); + + free(list); + list = NULL; +} + +void listEmpty(linkedList *list) +{ + if (NULL == list) + { + return; + } + + while (NULL != list->head) + { + listNode *pNode = list->head; + list->head = pNode->next; + if (NULL != list->free) + { + list->free(pNode->value); + } + else + { + free(pNode->value); + } + + pNode->next = NULL; + free(pNode); + pNode = NULL; + } +} + +linkedList * listAddNodeHead(linkedList *list, void * value) +{ + if (NULL == list || NULL == value) + { + return list; + } + + listNode *node = NULL; + node = malloc(sizeof(*node)); + if (NULL == node) + { + return list; + } + + node->value = value; + node->next = list->head; + list->head = node; + + ++list->len; + return list; +} + +linkedList * listAddNodeTail(linkedList *list, void *value) +{ + if (NULL == list || NULL == value) + { + return list; + } + + listNode *node = NULL; + node = malloc(sizeof(*node)); + if (NULL == node) + { + return list; + } + + node->value = value; + node->next = NULL; + + if (NULL == list->head + && list->len == 0) + { + list->head = node; + } + else + { + listNode *tail = list->head; + listNode *pre = list->head; + while (NULL != tail) + { + pre = tail; + tail = tail->next; + } + + pre->next = node; + } + + ++list->len; + return list; +} + +linkedList * listInsertNode(linkedList *list, listNode *old_node, void *value, bool after) +{ + if (NULL == list || NULL == old_node) + { + return list; + } + + listNode *pNode = NULL; + pNode = malloc(sizeof(*pNode)); + if (NULL == pNode) + { + return list; + } + + pNode->value = value; + if (after) + { + pNode->next = old_node->next; + old_node->next = pNode; + } + else + { + listNode *pre = list->head; + while (pre->next != old_node) + { + pre = pre->next; + } + + if (NULL != pre) + { + pre->next = pNode; + pNode->next = old_node; + } + } + + ++list->len; + return list; +} + +// ûͷźʱͷŴ +void listDelNode(linkedList *list, listNode *node) +{ + if (NULL == list || NULL == node) + { + return; + } + + listNode *pre = list->head; + listNode *cur = list->head; + while (NULL != cur && cur != node) + { + pre = cur; + cur = cur->next; + } + + // ڸ + if (NULL == pre) + { + return; + } + + pre->next = node->next; + node->next = NULL; + --list->len; + + if (NULL != list->free) + { + list->free(node->value); + free(node); + node = NULL; + } +} + +listNode * listSearchKey(linkedList *list, void *key) +{ + if (NULL == list) + { + return NULL; + } + + listNode *node = list->head; + while (NULL != node) + { + if (NULL != list->match) + { + if (list->match(key, node->value) == 0) + { + return node; + } + } + else + { + if (key == node->value) + { + return node; + } + } + + node = node->next; + } + + return NULL; +} + +listNode * listIndex(linkedList *list, long index) +{ + if (NULL == list) + { + return NULL; + } + + if (index <= 0 + || index > list->len) + { + return NULL; + } + + listNode *pNode = list->head; + for (long i = 0; i < index; ++i) + { + pNode = pNode->next; + } + + return pNode; +} + +linkedList* listRewind(linkedList *list) +{ + if (NULL == list) + { + return NULL; + } + + listNode *head = list->head; + listNode *pre = NULL; + listNode *next = NULL; + while (NULL != head) + { + next = head->next; + head->next = pre; + pre = head; + head = next; + } + + list->head = pre; + return list; +} + +size_t listLength(linkedList *list) +{ + if (NULL == list) + { + return 0; + } + + return list->len; +} \ No newline at end of file diff --git a/c-cpp/06_linkedlist/singlelist_gc/singleList.h b/c-cpp/06_linkedlist/singlelist_gc/singleList.h new file mode 100644 index 00000000..6617dbf7 --- /dev/null +++ b/c-cpp/06_linkedlist/singlelist_gc/singleList.h @@ -0,0 +1,46 @@ +#ifndef __SINGLELIST_H__ +#define __SINGLELIST_H__ + +#include +#include + +typedef struct listNode +{ + struct listNode *next; + void *value; +}listNode; + +typedef struct linkedList +{ + listNode *head; + size_t len; + size_t typesize; + + void(*dup)(void*, void*); + int(*match)(void*, void*); + void(*free)(void*); +}linkedList; + +#define listSetDupMethod(l,m) ((l)->dup = (m)) +#define listSetFreeMethod(l,m) ((l)->free = (m)) +#define listSetMatchMethod(l,m) ((l)->match = (m)) + +#define listGetDupMethod(l) ((l)->dup) +#define listGetFree(l) ((l)->free) +#define listGetMatchMethod(l) ((l)->match) + +linkedList *listCreate(); +void listRelease(linkedList *list); +void listEmpty(linkedList *list); +linkedList *listAddNodeHead(linkedList *list, void *value); +linkedList *listAddNodeTail(linkedList *list, void *value); +linkedList *listInsertNode(linkedList *list, listNode *old_node, void *value, bool after); +void listDelNode(linkedList *list, listNode *node); + +listNode *listSearchKey(linkedList *list, void *key); +listNode *listIndex(linkedList *list, long index); +linkedList* listRewind(linkedList *list); + +size_t listLength(linkedList *list); + +#endif // !__SINGLELIST_H__ diff --git a/c-cpp/07_linkedlist/LinkedListAlgo.c b/c-cpp/07_linkedlist/LinkedListAlgo.c index 159522fd..b326ee3d 100644 --- a/c-cpp/07_linkedlist/LinkedListAlgo.c +++ b/c-cpp/07_linkedlist/LinkedListAlgo.c @@ -79,6 +79,8 @@ void test_checkCircle() { SinglyLinkedNode* node = malloc(sizeof(SinglyLinkedNode)); node->data = i; current->next = node; + //reset current node + current = node; } current->next = h; diff --git a/c-cpp/07_linkedlist/SingleList.cpp b/c-cpp/07_linkedlist/SingleList.cpp index a43079cd..5f078638 100644 --- a/c-cpp/07_linkedlist/SingleList.cpp +++ b/c-cpp/07_linkedlist/SingleList.cpp @@ -6,7 +6,7 @@ using namespace std; class CElement; /*** - * @brief 单链表容器 + * @brief */ class CSingleList { @@ -15,61 +15,61 @@ class CSingleList ~CSingleList(); /** - * @brief 插入..链表末尾插入 - * @return 成功返回非空指针,否则失败 + * @brief ..ĩβ + * @return ɹطǿָ,ʧ */ CElement* Insert(void* lpData, int iDataSize); /** - * @brief 插入..链表指定位置插入 - * @return 成功返回非空指针,否则失败 + * @brief ..ָλò + * @return ɹطǿָ,ʧ */ CElement* Insert(CElement* lpElement, void* lpData, int iDataSize); /** - * @brief 删除 + * @brief ɾ */ void Delete(CElement*); /** - * @brief 链首 + * @brief */ CElement* Begin(); /** - * @brief 下一个元素 + * @brief һԪ */ CElement* Next(); /*** - * @brief 链尾 + * @brief β */ CElement* End(); /** - * @brief 是否是空链表 - * @return 空返回TRUE,否则返回FALSE + * @brief Ƿǿ + * @return շTRUE򷵻FALSE */ bool Empty(); /** - * @brief 反转 + * @brief ת */ void Reverse(); /** - * @brief 检测环 - * @return 返回TRUE时表示链表存在环,否则不存在环. + * @brief ⻷ + * @return TRUEʱʾڻ,򲻴ڻ. */ bool CheckCircle(); /** - * @brief 合并2个有序的链表 + * @brief ϲ2 */ void Merge(CSingleList& lst, std::function); /** - * @brief 删除倒数第K个结点 + * @brief ɾK */ void DeleteLastKth(int k); /** - * @brief 求中间节点 + * @brief мڵ */ CElement* Center(); private: @@ -80,18 +80,18 @@ class CSingleList CSingleList(CSingleList const & rhs); CSingleList& operator= (CSingleList const& rhs); private: - /**头结点*/ + /**ͷ*/ CElement* m_lpHead; - /**哨兵*/ + /**ڱ*/ CElement* m_lpSentinel; - /**空结点,用于End()返回 */ + /**ս㣬End() */ CElement* m_lpNull; - /**当前结点. 枚举时使用. */ + /**ǰ. öʱʹ. */ CElement* m_lpCur; }; /*** - * @brief 单链表结点元素. + * @brief Ԫ. */ class CElement { @@ -101,11 +101,11 @@ class CElement ~CElement(); public: /*** - * @brief 获取数据指针 + * @brief ȡָ */ void* GetDataPtr(); protected: - /**下一个结点*/ + /**һ*/ CElement* m_lpNext; void* m_lpData; }; @@ -113,7 +113,7 @@ class CElement void CreateList(CSingleList& lst) { - //循环插入元素到链表尾 + //ѭԪصβ for(int i=1; i<10;i++) { int* p = new int(); @@ -134,15 +134,15 @@ void PrintList(CSingleList& lst) int main() { { - /// 链表的基本操作,插入/枚举/删除 + /// Ļ/ö/ɾ CSingleList lst; CElement* lpElement = NULL; CreateList(lst); - std::cout<<"枚举链表当前的元素"<GetDataPtr())<GetDataPtr())<GetDataPtr())<GetDataPtr())<GetDataPtr())<GetDataPtr())<m_lpNext = lpCurElement->m_lpNext; lpCurElement->m_lpNext = lpNewElement; - }else{//插入到指定元素的前面 + }else{//뵽ָԪصǰ CElement* lpIter = m_lpSentinel; while(NULL != lpIter) { @@ -443,11 +443,13 @@ bool CSingleList::CheckCircle() return false; } +/** + * ϲ2 +*/ void CSingleList::Merge(CSingleList& lst, std::function fnCompare) { CElement* lpL1 = Begin(); CElement* lpL2 = lst.Begin(); - CElement* lpTail = NULL; if(!fnCompare) { @@ -458,7 +460,26 @@ void CSingleList::Merge(CSingleList& lst, std::function { if(lpL1 != End()) { + /** + * Ҫȷλ + * + * 1,2; 1 <- 2, 2ϲ1 + * + * 1ԪС2еԪأѭ1д2еĵǰԪصԪ + * 1ҵĵԪλ[A]ʱ2еĵǰԪز뵽Ԫλ[A]ǰ; + * 1вλ1ĩλԪ + */ iRet = fnCompare(lpL1->GetDataPtr(), lpL2->GetDataPtr()); + if(iRet < 0){ + lpL1 = Next(); + while(lpL1 != End()){ + iRet = fnCompare(lpL1->GetDataPtr(), lpL2->GetDataPtr()); + if(iRet > 0){ + break; + } + lpL1 = Next(); + } + } }else{ iRet = -1; } @@ -468,17 +489,13 @@ void CSingleList::Merge(CSingleList& lst, std::function lpNewElement->m_lpData = lpL2->GetDataPtr(); if(lpL1 != End()) { - Insert(lpNewElement,lpL1, iRet <= 0); + Insert(lpNewElement,lpL1, iRet < 0); }else{ - if(NULL == lpTail) - { - lpTail = Tail(); - } + CElement* lpTail = Tail(); Insert(lpNewElement,lpTail); } } lpL2 = lst.Next(); - lpL1 = Next(); } } diff --git a/c-cpp/07_linkedlist/linklist_jinshaohui.c b/c-cpp/07_linkedlist/linklist_jinshaohui.c new file mode 100644 index 00000000..a05c6cd0 --- /dev/null +++ b/c-cpp/07_linkedlist/linklist_jinshaohui.c @@ -0,0 +1,139 @@ +/************************************************************************* + > File Name: lisklist.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-07 + > Desc: + ************************************************************************/ +#include + + +struct stlistNode +{ + int val; + struct listNode *next; +}listNode; + +/*反转链表*/ +listNode reverseList(listNode *head) +{ + listNode *prev = NULL; + listNode *next = NULL; + + while(head != NULL) + { + next = head->next; + head->next = prev; + prev = head; + head = next; + } + + return prev; +} + +/*判断链表是否有环*/ +int hasCycle(listNode *head) +{ + listNode * fast = head; + listNode * low = head; + + while(fast != NULL && fast->next != NULL) + { + low = low->next; + fast = fast->next->next; + if (low == fast) + { + return 1; + } + } + + return 0; +} +/*合并有序链表*/ +listNode *mergeTwoLists(listNode *l1,listNode *l2) +{ + listNode head = {0}; + listNode *pRes = &head; + + while(1) + { + if(l1 == NULL) + { + pRes->next = l2; + } + + if (l2 == NULL) + { + pRes->next = l1; + } + + if(l1->val < l2->val) + { + pRes->next = l1; + l1 = l1->next; + } + else + { + pRes->next = l2; + l2 = l2->next; + } + pRes = pRes->next; + } + + return head; +} +/* + *删除链表倒数第n个节点,并返回链表头节点 */ + +listNode * removeNthFromEnd(listNode*headi,int n) +{ + listNode *fast = head; + listNode *prev = NULL; + listNpde *next = head; + int k = n; + + /*快指针往后移动k-1*/ + while((k > 1) && (fast != NULL)) + { + fast = fast->next; + k--; + } + + /*说明链表数目不足n个*/ + if (fast == NULL) + { + return head; + } + + while (fast->next != NULL) + { + fast = fast->next; + prev = next; + next = next->next; + } + + if(prev == NULL) + { + head = head->next; + } + else + { + prev->next = prev->next->next; + } + + return head; +} +/*求链表的中间节点*/ +listNode *middleNode(listNode *head) +{ + listNode * fast = head; + listNode * low = head; + + while(fast != NULL && fast->next != NULL) + { + low = low->next; + fast = fast->next->next; + } + + return low; +} diff --git a/c-cpp/08_stack/StackBasedOnArray/StackBasedOnArray.cpp b/c-cpp/08_stack/StackBasedOnArray/StackBasedOnArray.cpp new file mode 100644 index 00000000..d96ae5cc --- /dev/null +++ b/c-cpp/08_stack/StackBasedOnArray/StackBasedOnArray.cpp @@ -0,0 +1,122 @@ +/** + * 1)顺序栈的操作:入栈和出栈; + * 2)采用模板的方法实现存储任意类型的数据 + * 3)采用数组的栈,支持动态扩容,每次扩容1.5 倍,初始栈的大小是 10 。 + * + * Author:caozx + * time ;2018年10月11日 + */ + +#include +#include "StackBasedOnArray.h" +using namespace std; + +//构造函数,创建栈 +//类模板成员函数的写法 template 返回值类型 类名::成员函数名(参数列表){} +template ArrayStack::ArrayStack() +{ + this -> count = 10; + this -> flag = 0; + this -> array = new T[this -> count]; + if (! this -> array){ + cout << "array malloc memory failure" << endl; + } +} + + +//有参构造函数,创建栈 +template ArrayStack::ArrayStack(int count) +{ + this -> count = count; + this -> flag = 0; + this -> array = new T[this -> count]; + if (! this -> array){ + cout << "array malloc memory failure" << endl; + } +} + +//析构函数,销毁栈 +template ArrayStack::~ArrayStack(){ + this -> count = 0; + this -> flag = 0; + if(this -> array){ + delete [] this -> array; + this -> array = NULL; + } + +} + +// 入栈 +template void ArrayStack::push(T data){ + if(this -> flag == this -> count){ + cout << "The stack is full , so need to enlarge 1.5x! "<< endl; + this -> count = int (1.5 * this -> count); + T * temp = new T [this -> count]; + for(int i = 0; i < this -> flag ; i++){ + temp[i] = this -> array[i]; + //cout << temp[i] < array; //释放原来的空间 + temp[this -> flag] = data; + this -> flag ++; + this -> array = temp; + } + else{ + this -> array [this -> flag] = data; + this -> flag ++ ; + } +} + +//出栈,并删除栈顶元素 +template T ArrayStack::pop(){ + this -> flag --; + T temp = this -> array[this -> flag]; + return temp; +} + +//出栈,不删除栈顶元素 +template T ArrayStack::peek(){ + T temp = this -> array[this -> flag - 1]; + return temp; +} + +template int ArrayStack::stackSize(){ + return this -> flag; +} + +template int ArrayStack::stackMaxSize(){ + return this -> count; +} + +int main(int argc, char const *argv[]) +{ + cout << " === test begin ===" << endl; + ArrayStack arrstack(12); + arrstack.push(10); + arrstack.push(20); + arrstack.push(30); + arrstack.push(40); + arrstack.push(50); + arrstack.push(60); + arrstack.push(70); + arrstack.push(80); + arrstack.push(90); + arrstack.push(100); + arrstack.push(110); + arrstack.push(120); + arrstack.push(130); + arrstack.push(140); + arrstack.push(150); + + cout << "peek , not delete " << arrstack.peek() << endl; + cout << "pop , delete " << arrstack.pop()< class 类名{} +template class ArrayStack +{ +public: + ArrayStack(); + ArrayStack(int count); + ~ArrayStack(); + void push(T data); //入栈 + T pop(); //出栈,并删除栈顶元素 + T peek(); //返回栈顶元素,不删除栈顶元素,栈顶指针不变 + int stackSize(); + int stackMaxSize(); + +private: + int flag; //栈顶标签,指向栈顶 + int count ; //栈的容量 + T *array; //指针 +}; diff --git a/c-cpp/08_stack/StackBasedOnLinkedList/StackBasedOnLinkedList.cpp b/c-cpp/08_stack/StackBasedOnLinkedList/StackBasedOnLinkedList.cpp new file mode 100644 index 00000000..827abc20 --- /dev/null +++ b/c-cpp/08_stack/StackBasedOnLinkedList/StackBasedOnLinkedList.cpp @@ -0,0 +1,127 @@ +/** + * 1)链式栈的操作:入栈,出栈以及返回栈的大小; + * 2)采用模板的方法实现存储任意类型的数据 + * 3)采用单链表实现栈 + * 4)pop和peek 出栈的返回值稍微有点问题,当栈为空的时候,返回null,cpp默认返回的是0。 + * * 改进方法就是不用函数的返回值返回栈顶元素,而是采用参数列表的形式返回,这样稍微有点麻烦 + * * 或者就是在使用的时候先调用size函数判断以下 + * Author:caozx + * time ;2018年10月11日 + */ + +#include +#include "StackBasedOnLinkedList.h" +using namespace std; + +template LinkedListStack::LinkedListStack() +{ + this -> count = 0; + this -> head = new LinkedNode; + this -> head -> next = NULL; +} + +template LinkedListStack::~LinkedListStack() +{ + LinkedNode * ptr, * temp; + ptr = head; + while(ptr -> next != NULL){ + temp = ptr -> next; + ptr -> next = temp -> next; + delete temp; + } + delete head ; //删除头节点 + this -> head = NULL; + this -> count = 0; +} + +// 入栈 +template void LinkedListStack::push(const T & data) +{ + LinkedNode * insertPtr = new LinkedNode; + insertPtr -> data = data; + insertPtr -> next = this -> head -> next; + head -> next = insertPtr; + this -> count ++; + cout << "push data : " << this -> head -> next -> data << endl; +} + +//返回栈顶元素,即出栈,不删除栈顶元素 +template T LinkedListStack::peek() +{ + if(this -> count == 0 || this -> head -> next == NULL){ + cout << " stack is empty, peek fail"<< endl; + return NULL; + } + else{ + return this -> head -> next -> data; + } +} + +//出栈,删除栈顶元素 +template T LinkedListStack::pop() +{ + if(this -> count == 0 || this -> head -> next == NULL){ + cout << " stack is empty, pop fail"<< endl; + return NULL; + } + else{ + LinkedNode * temp = this -> head -> next; + this -> head -> next = temp -> next; + T data = temp -> data; + delete temp; + this -> count --; + return data; + } + +} + +//返回栈的大小 +template int LinkedListStack::size() const +{ + return this -> count; +} + +int main(int argc, char const *argv[]) +{ + cout << " === StackBasedOnLinkedList test begin ===" << endl; + LinkedListStack stack; + cout << "size==="< class LinkedListStack +{ +public: + LinkedListStack(); + ~LinkedListStack(); + + void push(const T & data); //入栈 + T peek(); //返回栈顶元素,即出栈,不删除栈顶元素 + T pop(); //出栈,删除栈顶元素 + int size() const; //返回栈的大小 +private: + int count; //存放栈的大小,因为是单链表所以这里不规定栈的最大可承载量 + struct LinkedNode + { + T data; + LinkedNode * next; + }; + LinkedNode * head; // 单链表的头指针,不带头节点 +}; diff --git a/c-cpp/08_stack/arrayStack/arrayStack.c b/c-cpp/08_stack/arrayStack/arrayStack.c new file mode 100644 index 00000000..fec2555d --- /dev/null +++ b/c-cpp/08_stack/arrayStack/arrayStack.c @@ -0,0 +1,161 @@ +/************************************************************************* + > File Name: arrayStack.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-12 + > Desc: 数组实现顺序栈 + ************************************************************************/ +#include +#include +#include +#include"./arrayStack.h" + +/*创建并初始化顺序栈*/ +stArrayStack * arrayStack_create(int size) +{ + stArrayStack *parrStack = NULL; + + parrStack = (stArrayStack *)malloc(sizeof(stArrayStack)); + if (parrStack == NULL) + { + return NULL; + } + + parrStack->size = size; + parrStack->pos = -1; + parrStack->array = (int *)malloc(sizeof(int)*size); + if(parrStack->array == NULL) + { + free(parrStack); + return NULL; + } + + return parrStack; +} +/*销毁顺序栈*/ +void arrayStack_destory(stArrayStack * parrStack) +{ + if(parrStack == NULL) + { + return; + } + + if (parrStack->array != NULL) + { + free(parrStack->array); + } + + free(parrStack); + return; +} +/*出栈*/ +int arrayStack_pop(stArrayStack *parrStack) +{ + int data = 0; + + if(arrayStack_is_empty(parrStack)) + { + return -1; + } + data = parrStack->array[parrStack->pos]; + parrStack->pos--; + + return data; +} +/*入栈*/ +int arrayStack_push(stArrayStack *parrStack,int data) +{ + if(arrayStack_is_full(parrStack)) + { + return -1; + } + + parrStack->pos++; + parrStack->array[parrStack->pos] = data; + + return 0; +} + +int arrayStack_push_new(stArrayStack*parrStack,int data) +{ + int *ptmp = NULL; + + /*如果栈不满,直接插入*/ + if(!arrayStack_is_full(parrStack)) + { + return arrayStack_push(parrStack,data); + } + + /*如果栈已经满,申请内存*/ + ptmp = (int *)malloc(2*parrStack->size*sizeof(int)); + if (ptmp == NULL) + { + return -1; + } + + memcpy(ptmp,parrStack->array,parrStack->size*sizeof(int)); + + free(parrStack->array); + + parrStack->array = ptmp; + parrStack->size = 2*parrStack->size; + parrStack->pos++; + parrStack->array[parrStack->pos] = data; + + return ; +} + +void arrayStack_dump(stArrayStack *parrStack) +{ + int i = 0; + + if (arrayStack_is_empty(parrStack)) + { + printf("\r\n arrayStack is empty."); + return; + } + printf("\r\narrayStack size = %d,pos= %d,", + parrStack->size,parrStack->pos); + for(i = 0; i <= parrStack->pos; i++) + { + printf("\r\narry[%d] = %d",i,parrStack->array[i]); + } +} + +int main() +{ + int i = 0; + int ret = 0; + stArrayStack * parrStack = NULL; + + printf("\r\n create size = 4 arrayStack."); + + parrStack = arrayStack_create(4); + if (parrStack == NULL) + { + printf("\r\n create size = 4 arrayStack faided."); + return 0; + } + + for (i = 0; i < 5; i++) + { + ret = arrayStack_push(parrStack,i); + if(ret != 0) + { + printf("\r\n push size = %d arrayStack faided.",i); + + } + } + arrayStack_dump(parrStack); + + ret = arrayStack_push_new(parrStack,4); + if(ret != 0) + { + printf("\r\n push size = %d arrayStack faided.",4); + } + arrayStack_dump(parrStack); + + arrayStack_destory(parrStack); + + return; +} diff --git a/c-cpp/08_stack/arrayStack/arrayStack.h b/c-cpp/08_stack/arrayStack/arrayStack.h new file mode 100644 index 00000000..34e95aa6 --- /dev/null +++ b/c-cpp/08_stack/arrayStack/arrayStack.h @@ -0,0 +1,23 @@ +/************************************************************************* + > File Name: arrayStack.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-12 + > Desc: + ************************************************************************/ + +#ifndef ARRAY_STACJ_H +#define ARRAY_STACJ_H + +typedef struct _array_stack +{ + int size;/*栈的大小*/ + int pos;/*当前存储元素的个数,即栈顶元素下表*/ + int *array;/*数据存储区*/ +}stArrayStack; + +#define arrayStack_size(arrayStack) (arrayStack->size) +#define arrayStack_is_empty(arrayStack) (arrayStack->pos == -1) +#define arrayStack_is_full(arrayStack) (arrayStack->pos == (arrayStack->size-1)) + +#endif diff --git a/c-cpp/08_stack/linkList/linklist_stack.c b/c-cpp/08_stack/linkList/linklist_stack.c new file mode 100644 index 00000000..08846bd0 --- /dev/null +++ b/c-cpp/08_stack/linkList/linklist_stack.c @@ -0,0 +1,136 @@ +/************************************************************************* + > File Name: linklist_stack.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-12 + > Desc: + ************************************************************************/ +#include +#include +#include +#include "./linklist_stack.h" + +linklist_stack * stack_create() +{ + linklist_stack * stack = NULL; + + stack = (linklist_stack *)malloc(sizeof(linklist_stack)); + if (stack == NULL) + { + return NULL; + } + + stack->next = NULL; + + return stack; +} + +void stack_destory(linklist_stack* stack) +{ + linklist_stack * ptmp = NULL; + + while(!stack_is_empty(stack)) + { + ptmp = stack->next; + stack->next = stack->next->next; + + free(ptmp); + } + + free(stack); + + return; +} + +int stack_push(linklist_stack *stack,int data) +{ + linklist_stack * ptmp = NULL; + + ptmp = (linklist_stack *)malloc(sizeof(linklist_stack)); + if (ptmp == NULL) + { + return -1; + } + + ptmp->data = data; + ptmp->next = stack->next; + stack->next = ptmp; + + return 0; +} + +int stack_pop(linklist_stack*stack,int *data) +{ + linklist_stack *ptmp = NULL; + if (data == NULL) + { + return -1; + } + if(stack_is_empty(stack)) + { + return -1; + } + *data = stack->next->data; + ptmp = stack->next; + stack->next = ptmp->next; + free(ptmp); + + return 0; +} + + +void stack_dump(linklist_stack *stack) +{ + linklist_stack * ptmp = stack->next; + + while(ptmp != NULL) + { + printf("\r\n data = %d",ptmp->data); + ptmp = ptmp->next; + } + return; +} + +int main() +{ + int i = 0; + int ret = 0; + int data = 0; + linklist_stack * stack = NULL; + + stack = stack_create(); + if (stack == NULL) + { + printf("\r\n stack create falied."); + return 0; + } + + for (i = 0; i < 4; i++) + { + ret = stack_push(stack,i); + if(ret != 0) + { + printf("\r\n stack push %d falied.",i); + } + } + + stack_dump(stack); + + for (i = 0; i < 5; i++) + { + ret = stack_pop(stack,&data); + if(ret != 0) + { + printf("\r\n stack pop%d falied.", i); + } + else + { + printf("\r\n data = %d,",data); + } + } + + stack_destory(stack); + + return 0; + +} diff --git a/c-cpp/08_stack/linkList/linklist_stack.h b/c-cpp/08_stack/linkList/linklist_stack.h new file mode 100644 index 00000000..43123b5b --- /dev/null +++ b/c-cpp/08_stack/linkList/linklist_stack.h @@ -0,0 +1,22 @@ +/************************************************************************* + > File Name: linklist_stack.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-12 + > Desc: + ************************************************************************/ + +#ifndef STACK_LINK_LIST_H +#define STACK_LINK_LIST_H + +typedef struct _linkliststack +{ + int data; + struct _linkliststack *next; +}linklist_stack; + + +#define stack_is_empty(liststack) (liststack->next == NULL) + +#endif + diff --git a/c-cpp/09_queue/.gitkeep b/c-cpp/09_queue/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/09_queue/array_queue.hpp b/c-cpp/09_queue/array_queue.hpp new file mode 100644 index 00000000..6bf0892e --- /dev/null +++ b/c-cpp/09_queue/array_queue.hpp @@ -0,0 +1,97 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/10. + */ + +#ifndef QUEUE_ARRAY_QUEUE_HPP_ +#define QUEUE_ARRAY_QUEUE_HPP_ + +template +class ArrayQueue { + private: + T* items_ = nullptr; + size_t capacity_ = 0; + size_t head_ = 0; + size_t tail_ = 0; + + public: + ArrayQueue() = delete; + ArrayQueue(const size_t capacity) : capacity_(capacity) { + items_ = new T[capacity_]; + } + ~ArrayQueue() { + if (nullptr != items_) { + delete[] items_; + items_ = nullptr; + } + } + ArrayQueue(const ArrayQueue& other) : capacity_(other.capacity_) { + items_ = new T[capacity_]; + for (size_t i = other.head_; i != other.tail_; ++i) { + enqueue(other.items_[i]); + } + } + ArrayQueue& operator=(const ArrayQueue& rhs) { + delete[] items_; + head_ = 0; + tail_ = 0; + capacity_ = rhs.capacity_; + items_ = new T[capacity_]; + for (size_t i = rhs.head_; i != rhs.tail_; ++i) { + enqueue(rhs.items_[i]); + } + return *this; + } + ArrayQueue(ArrayQueue&& other) : items_(other.items_), + capacity_(other.capacity_), + head_(other.head_), + tail_(other.tail_) { + other.items_ = nullptr; + other.capacity_ = 0; + other.head_ = 0; + other.tail_ = 0; + } + ArrayQueue& operator=(ArrayQueue&& rhs) { + delete[] items_; + items_ = rhs.items_; + capacity_ = rhs.capacity_; + head_ = rhs.head_; + tail_ = rhs.tail_; + rhs.items_ = nullptr; + rhs.capacity_ = 0; + rhs.head_ = 0; + rhs.tail_ = 0; + return *this; + } + + public: + void enqueue(T item) { + if (capacity_ == tail_) { + throw "Push data into a full queue!"; + } + items_[tail_++] = item; + } + T head() const { + if (head_ != tail_) { + return items_[head_]; + } else { + throw "Fetch data from an empty queue!"; + } + } + void dequeue() { + if (head_ != tail_) { + ++head_; + } else { + throw "Pop data from an empty queue!"; + } + } + + public: + template + void traverse(UnaryFunc do_traverse) { + for (size_t i = head_; i != tail_; ++i) { + do_traverse(items_[i]); + } + } +}; + +#endif // QUEUE_ARRAY_QUEUE_HPP_ diff --git a/c-cpp/09_queue/array_queue/array_queue.c b/c-cpp/09_queue/array_queue/array_queue.c new file mode 100644 index 00000000..d593983d --- /dev/null +++ b/c-cpp/09_queue/array_queue/array_queue.c @@ -0,0 +1,158 @@ +/************************************************************************* + > File Name: array_queue.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-12 + > Desc: + ************************************************************************/ +#include +#include +#include +#include"./array_queue.h" + + +array_queue * array_queue_create(int size) +{ + array_queue * queue = NULL; + + queue = (array_queue*)malloc(sizeof(array_queue)); + if (queue == NULL) + { + return NULL; + } + queue->array = (int *)malloc(sizeof(int)*size); + if (queue->array == NULL) + { + free(queue); + return NULL; + } + queue->size = size; + queue->num = 0; + queue->head = 0; + queue->tail = 0; + + return queue; +} + +void array_queue_destory(array_queue *queue) +{ + if (queue == NULL) + { + return; + } + + if (queue->array != NULL) + { + free(queue->array); + } + + free(queue); + return; +} + +/*入队列 */ +int array_queue_enqueue(array_queue *queue,int data) +{ + /*队列为空,或者队列满时,返回-1*/ + if ((queue == NULL) || (array_queue_is_full(queue))) + { + return -1; + } + + queue->num++; + queue->array[queue->tail] = data; + queue->tail = (queue->tail + 1) % queue->size; + + return 0; +} + +/*出队列*/ +int array_queue_dequeue(array_queue * queue,int *data) +{ + /*队列为空,数据存储为空,队列为空时返回-1*/ + if ((queue == NULL) || (data == NULL) || (array_queue_is_empty(queue))) + { + return -1; + } + *data = queue->array[queue->head]; + queue->num--; + queue->head = (queue->head + 1) % queue->size; + + return 0; +} + +void array_queue_dump(array_queue *queue) +{ + int i = 0; + int pos = 0; + + if ((queue == NULL) || (array_queue_is_empty(queue))) + { + printf("\r\n queue is empty"); + return; + } + + printf("\r\n size:%d,num:%d,head:%d,tali:%d", + queue->size,queue->num,queue->head,queue->tail); + for (i = 0; i < queue->num; i ++) + { + pos = (queue->head + i) %queue->size; + printf("\r\n array[%d] = %d",pos,queue->array[pos]); + } + return; +} + +int main() +{ + int i = 0; + int ret = 0; + int data = 0; + array_queue * queue = NULL; + + queue = array_queue_create(4); + if (queue == NULL) + { + printf("\r\n queue is create failed."); + return 0; + } + /*队列时空时,出队返回错误*/ + ret = array_queue_dequeue(queue, &data); + if (ret != 0) + { + printf("\r\n queue %d dequeue failed.",ret); + } + + /*队列大小是4,入队5个,最后一个报错*/ + for (i = 0; i < 5; i++) + { + ret = array_queue_enqueue(queue,i); + if (ret != 0) + { + printf("\r\n queue %d enqueue failed.",i); + + } + } + + array_queue_dump(queue); + + ret = array_queue_dequeue(queue, &data); + if (ret != 0) + { + printf("\r\n queue %d dequeue failed.",i); + } + printf("\r\n queue %d dequue.",data); + array_queue_dump(queue); + data = 5; + printf("\r\n queue %d enqueue.",data); + ret = array_queue_enqueue(queue,data); + if (ret != 0) + { + printf("\r\n queue %d enqueue failed.",data); + } + array_queue_dump(queue); + + array_queue_destory(queue); + return 0; + +} + diff --git a/c-cpp/09_queue/array_queue/array_queue.h b/c-cpp/09_queue/array_queue/array_queue.h new file mode 100644 index 00000000..e8e68fee --- /dev/null +++ b/c-cpp/09_queue/array_queue/array_queue.h @@ -0,0 +1,24 @@ +/************************************************************************* + > File Name: array_queue.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-12 + > Desc: + ************************************************************************/ +#ifndef ARRAY_QUEUE_H +#define ARRAY_QUEUE_H + +typedef struct _array_queue +{ + int size;/*队列的大小*/ + int num; /*当前存储数据的大小*/ + int head;/*队列的头*/ + int tail;/*队列的尾*/ + int *array;/*数据存储区*/ +}array_queue; + +#define array_queue_is_empty(array_queue) (array_queue->num == 0) +#define array_queue_is_full(array_queue) ((array_queue->num) == (array_queue->size)) + +#endif + diff --git a/c-cpp/09_queue/array_queue_test.cc b/c-cpp/09_queue/array_queue_test.cc new file mode 100644 index 00000000..c2c3b3b2 --- /dev/null +++ b/c-cpp/09_queue/array_queue_test.cc @@ -0,0 +1,56 @@ +#include +#include "array_queue.hpp" + +int main() { + auto do_traverse = [&](auto item){ std::cout << item << ' '; }; + + ArrayQueue array_queue_1(3); + array_queue_1.enqueue(1); + array_queue_1.enqueue(2); + array_queue_1.enqueue(3); + // array_queue_1.enqueue(4); // throw + array_queue_1.traverse(do_traverse); + std::cout << std::endl; + + ArrayQueue array_queue_2(array_queue_1); // copy constructor + array_queue_2.traverse(do_traverse); + std::cout << std::endl; + + ArrayQueue array_queue_3(std::move(array_queue_2)); // move constructor + array_queue_3.traverse(do_traverse); + std::cout << std::endl; + array_queue_2.traverse(do_traverse); + std::cout << std::endl; + + std::cout << array_queue_3.head() << std::endl; + array_queue_3.dequeue(); + std::cout << array_queue_3.head() << std::endl; + array_queue_3.dequeue(); + std::cout << array_queue_3.head() << std::endl; + array_queue_3.dequeue(); + // std::cout << array_queue_3.head() << std::endl; // throw + // array_queue_3.dequeue(); // throw + + ArrayQueue array_queue_4(1); + array_queue_4 = array_queue_1; // copy assignment + array_queue_4.traverse(do_traverse); + std::cout << std::endl; + + ArrayQueue array_queue_5(100); + array_queue_5 = std::move(array_queue_4); // move assignment + array_queue_5.traverse(do_traverse); + std::cout << std::endl; + array_queue_4.traverse(do_traverse); + std::cout << std::endl; + + std::cout << array_queue_5.head() << std::endl; + array_queue_5.dequeue(); + std::cout << array_queue_5.head() << std::endl; + array_queue_5.dequeue(); + std::cout << array_queue_5.head() << std::endl; + array_queue_5.dequeue(); + // std::cout << array_queue_5.head() << std::endl; // throw + // array_queue_5.dequeue(); // throw + + return 0; +} diff --git a/c-cpp/09_queue/block_queue.hpp b/c-cpp/09_queue/block_queue.hpp new file mode 100644 index 00000000..d8de1689 --- /dev/null +++ b/c-cpp/09_queue/block_queue.hpp @@ -0,0 +1,82 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/11. + */ + +#ifndef QUEUE_BLOCK_QUEUE_HPP_ +#define QUEUE_BLOCK_QUEUE_HPP_ + +#include +#include +#include + +template +class BlockQueue { + public: + using value_type = T; + using container_type = std::queue; + using size_type = typename container_type::size_type; + + private: + size_type capacity_ = 0; + container_type container_; + mutable std::mutex mutex_; + mutable std::condition_variable not_empty_; + mutable std::condition_variable not_full_; + + public: + BlockQueue() = delete; + BlockQueue(const size_type capacity) : capacity_(capacity) {} + BlockQueue(const BlockQueue&) = default; + BlockQueue(BlockQueue&&) = default; + BlockQueue& operator=(const BlockQueue&) = default; + BlockQueue& operator=(BlockQueue&&) = default; + + private: + bool empty() const { return container_.empty(); } + bool full() const { return not(container_.size() < capacity_); } + + public: + void put(const value_type& item) { + std::unqiue_lock lock(mutex_); + while (full()) { + not_full_.wait(lock); + } + container_.push(item); + not_empty_.notify_one(); + } + void take(value_type& out) { + std::unique_lock lock(mutex_); + while (empty()) { + not_empty_.wait(lock); + } + out = container_.front(); + container_.pop(); + not_full_.notify_one(); + } + template + bool put_for(const value_type& item, const Duration& d) { + std::unqiue_lock lock(mutex_); + if (not_full_.wait_for(lock, d, [&](){ return not full(); })) { + container_.push(item); + not_empty_.notify_one(); + return true; + } else { + return false; + } + } + template + bool take_for(const Duration& d, value_type& out) { + std::unique_lock lock(mutex_); + if (not_empty_.wait_for(lock, d, [&](){ return not empty(); })) { + out = container_.front(); + container_.pop(); + not_full_.notify_one(); + return true; + } else { + return false; + } + } +}; + +#endif // QUEUE_BLOCK_QUEUE_HPP_ + diff --git a/c-cpp/09_queue/circular_queue.hpp b/c-cpp/09_queue/circular_queue.hpp new file mode 100644 index 00000000..df4664cc --- /dev/null +++ b/c-cpp/09_queue/circular_queue.hpp @@ -0,0 +1,99 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/10. + */ + +#ifndef QUEUE_CIRCULAR_QUEUE_HPP_ +#define QUEUE_CIRCULAR_QUEUE_HPP_ + +template +class CircularQueue { + private: + T* items_ = nullptr; + size_t capacity_ = 0; + size_t head_ = 0; + size_t tail_ = 0; + + public: + CircularQueue() = delete; + CircularQueue(const size_t capacity) : capacity_(capacity) { + items_ = new T[capacity_]; + } + ~CircularQueue() { + if (nullptr != items_) { + delete[] items_; + items_ = nullptr; + } + } + CircularQueue(const CircularQueue& other) : capacity_(other.capacity_) { + items_ = new T[capacity_]; + for (size_t i = other.head_; i != other.tail_; ++i) { + enqueue(other.items_[i]); + } + } + CircularQueue& operator=(const CircularQueue& rhs) { + delete[] items_; + head_ = 0; + tail_ = 0; + capacity_ = rhs.capacity_; + items_ = new T[capacity_]; + for (size_t i = rhs.head_; i != rhs.tail_; ++i) { + enqueue(rhs.items_[i]); + } + return *this; + } + CircularQueue(CircularQueue&& other) : items_(other.items_), + capacity_(other.capacity_), + head_(other.head_), + tail_(other.tail_) { + other.items_ = nullptr; + other.capacity_ = 0; + other.head_ = 0; + other.tail_ = 0; + } + CircularQueue& operator=(CircularQueue&& rhs) { + delete[] items_; + items_ = rhs.items_; + capacity_ = rhs.capacity_; + head_ = rhs.head_; + tail_ = rhs.tail_; + rhs.items_ = nullptr; + rhs.capacity_ = 0; + rhs.head_ = 0; + rhs.tail_ = 0; + return *this; + } + + public: + void enqueue(T item) { + if ((tail_ + 1) % capacity_ == head_) { + throw "Push data into a full queue!"; + } + items_[tail_] = item; + tail_ = (tail_ + 1) % capacity_; + } + T head() const { + if (head_ != tail_) { + return items_[head_]; + } else { + throw "Fetch data from an empty queue!"; + } + } + void dequeue() { + if (head_ != tail_) { + head_ = (head_ + 1) % capacity_; + } else { + throw "Pop data from an empty queue!"; + } + } + + public: + template + void traverse(UnaryFunc do_traverse) { + if (0 == capacity_) return; + for (size_t i = head_; i % capacity_ != tail_; ++i) { + do_traverse(items_[i % capacity_]); + } + } +}; + +#endif // QUEUE_CIRCULAR_QUEUE_HPP_ diff --git a/c-cpp/09_queue/circular_queue_test.cc b/c-cpp/09_queue/circular_queue_test.cc new file mode 100644 index 00000000..46e025ae --- /dev/null +++ b/c-cpp/09_queue/circular_queue_test.cc @@ -0,0 +1,62 @@ +#include +#include "circular_queue.hpp" + +int main() { + auto do_traverse = [&](auto item){ std::cout << item << ' '; }; + + CircularQueue circular_queue_1(4); + circular_queue_1.enqueue(1); + circular_queue_1.enqueue(2); + circular_queue_1.enqueue(3); + // circular_queue_1.enqueue(4); // throw + circular_queue_1.traverse(do_traverse); + std::cout << std::endl; + + CircularQueue circular_queue_2(circular_queue_1); // copy constructor + circular_queue_2.traverse(do_traverse); + std::cout << std::endl; + + CircularQueue circular_queue_3(std::move(circular_queue_2)); // move constructor + circular_queue_3.traverse(do_traverse); + std::cout << std::endl; + circular_queue_2.traverse(do_traverse); + std::cout << std::endl; + + std::cout << circular_queue_3.head() << std::endl; + circular_queue_3.dequeue(); + std::cout << circular_queue_3.head() << std::endl; + circular_queue_3.dequeue(); + std::cout << circular_queue_3.head() << std::endl; + circular_queue_3.dequeue(); + // std::cout << circular_queue_3.head() << std::endl; // throw + // circular_queue_3.dequeue(); // throw + + CircularQueue circular_queue_4(1); + circular_queue_4 = circular_queue_1; // copy assignment + circular_queue_4.traverse(do_traverse); + std::cout << std::endl; + + CircularQueue circular_queue_5(100); + circular_queue_5 = std::move(circular_queue_4); // move assignment + circular_queue_5.traverse(do_traverse); + std::cout << std::endl; + circular_queue_4.traverse(do_traverse); + std::cout << std::endl; + + std::cout << circular_queue_5.head() << std::endl; + circular_queue_5.dequeue(); + std::cout << circular_queue_5.head() << std::endl; + circular_queue_5.dequeue(); + std::cout << circular_queue_5.head() << std::endl; + circular_queue_5.dequeue(); + // std::cout << circular_queue_5.head() << std::endl; // throw + // circular_queue_5.dequeue(); // throw + + for (size_t i = 0; i != 4; ++i) { + circular_queue_1.dequeue(); + circular_queue_1.enqueue(i + 4); + circular_queue_1.traverse(do_traverse); + std::cout << std::endl; + } + return 0; +} diff --git a/c-cpp/09_queue/concurrency_queue.hpp b/c-cpp/09_queue/concurrency_queue.hpp new file mode 100644 index 00000000..8e9bb787 --- /dev/null +++ b/c-cpp/09_queue/concurrency_queue.hpp @@ -0,0 +1,85 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/11. + */ + +#ifndef QUEUE_CONCURRENCY_QUEUE_HPP_ +#define QUEUE_CONCURRENCY_QUEUE_HPP_ + +#include +#include +#include +#include + +template +class ConcurrencyQueue { + public: + using value_type = T; + using container_type = std::queue; + using size_type = typename container_type::size_type; + + private: + container_type container_; + mutable std::mutex mutex_; + std::condition_variable container_cond_; + + public: + ConcurrencyQueue() = default; + ConcurrencyQueue(const ConcurrencyQueue&) = default; + ConcurrencyQueue(ConcurrencyQueue&&) = default; + ConcurrencyQueue& operator=(const ConcurrencyQueue&) = default; + ConcurrencyQueue& operator=(ConcurrencyQueue&&) = default; + + private: + bool empty_() const { return container_.empty(); } + + public: + bool empty() const { + std::lock_guard lg(mutex_); + return container_.empty(); + } + void push(value_type item) { + std::lock_guard lg(mutex_); + container_.push(std::move(item)); + container_cond_.notify_one(); + } + void wait_and_pop(value_type& out) { + std::unique_lock lk(mutex_); + while (empty_()) { + container_cond_.wait(lk) + } + out = std::move(container_.front()); + container_.pop(); + } + std::shared_ptr wait_and_pop() { + std::unique_lock lk(mutex_); + while (empty_()) { + container_cond_.wait(lk) + } + auto res = std::make_shared(std::move(container_.front())); + container_.pop(); + return res; + } + bool try_pop(value_type& out) { + std::lock_guard lg(mutex_); + if (empty_()) { + return false; + } else { + out = std::move(container_.front()); + container_.pop(); + return true; + } + } + std::shared_ptr try_pop() { + std::lock_guard lg(mutex_); + if (empty_()) { + return nullptr; + } else { + auto res = std::make_shared(std::move(container_.front())); + container_.pop(); + return res; + } + } +}; + +#endif // QUEUE_CONCURRENCY_QUEUE_HPP_ + diff --git a/c-cpp/09_queue/dynamic_array_queue.hpp b/c-cpp/09_queue/dynamic_array_queue.hpp new file mode 100644 index 00000000..73939e40 --- /dev/null +++ b/c-cpp/09_queue/dynamic_array_queue.hpp @@ -0,0 +1,105 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/10. + */ + +#ifndef QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_ +#define QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_ + +template +class DynamicArrayQueue { + private: + T* items_ = nullptr; + size_t capacity_ = 0; + size_t head_ = 0; + size_t tail_ = 0; + + public: + DynamicArrayQueue() = delete; + DynamicArrayQueue(const size_t capacity) : capacity_(capacity) { + items_ = new T[capacity_]; + } + ~DynamicArrayQueue() { + if (nullptr != items_) { + delete[] items_; + items_ = nullptr; + } + } + DynamicArrayQueue(const DynamicArrayQueue& other) : capacity_(other.capacity_) { + items_ = new T[capacity_]; + for (size_t i = other.head_; i != other.tail_; ++i) { + enqueue(other.items_[i]); + } + } + DynamicArrayQueue& operator=(const DynamicArrayQueue& rhs) { + delete[] items_; + head_ = 0; + tail_ = 0; + capacity_ = rhs.capacity_; + items_ = new T[capacity_]; + for (size_t i = rhs.head_; i != rhs.tail_; ++i) { + enqueue(rhs.items_[i]); + } + return *this; + } + DynamicArrayQueue(DynamicArrayQueue&& other) : items_(other.items_), + capacity_(other.capacity_), + head_(other.head_), + tail_(other.tail_) { + other.items_ = nullptr; + other.capacity_ = 0; + other.head_ = 0; + other.tail_ = 0; + } + DynamicArrayQueue& operator=(DynamicArrayQueue&& rhs) { + delete[] items_; + items_ = rhs.items_; + capacity_ = rhs.capacity_; + head_ = rhs.head_; + tail_ = rhs.tail_; + rhs.items_ = nullptr; + rhs.capacity_ = 0; + rhs.head_ = 0; + rhs.tail_ = 0; + return *this; + } + + public: + void enqueue(T item) { + if (capacity_ == tail_ - head_) { + throw "Push data into a full queue!"; + } + if (capacity_ == tail_) { + // item transport + for (size_t i = head_; i != tail_; ++i) { + items_[i - head_] = items_[i]; + } + tail_ = tail_ - head_; + head_ = 0; + } + items_[tail_++] = item; + } + T head() const { + if (head_ != tail_) { + return items_[head_]; + } else { + throw "Fetch data from an empty queue!"; + } + } + void dequeue() { + if (head_ != tail_) { + ++head_; + } else { + throw "Pop data from an empty queue!"; + } + } + + public: + template + void traverse(UnaryFunc do_traverse) { + for (size_t i = head_; i != tail_; ++i) { + do_traverse(items_[i]); + } + } +}; + +#endif // QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_ diff --git a/c-cpp/09_queue/dynamic_array_queue_test.cc b/c-cpp/09_queue/dynamic_array_queue_test.cc new file mode 100644 index 00000000..65804338 --- /dev/null +++ b/c-cpp/09_queue/dynamic_array_queue_test.cc @@ -0,0 +1,62 @@ +#include +#include "dynamic_array_queue.hpp" + +int main() { + auto do_traverse = [&](auto item){ std::cout << item << ' '; }; + + DynamicArrayQueue dynamic_array_queue_1(3); + dynamic_array_queue_1.enqueue(1); + dynamic_array_queue_1.enqueue(2); + dynamic_array_queue_1.enqueue(3); + // dynamic_array_queue_1.enqueue(4); // throw + dynamic_array_queue_1.traverse(do_traverse); + std::cout << std::endl; + + DynamicArrayQueue dynamic_array_queue_2(dynamic_array_queue_1); // copy constructor + dynamic_array_queue_2.traverse(do_traverse); + std::cout << std::endl; + + DynamicArrayQueue dynamic_array_queue_3(std::move(dynamic_array_queue_2)); // move constructor + dynamic_array_queue_3.traverse(do_traverse); + std::cout << std::endl; + dynamic_array_queue_2.traverse(do_traverse); + std::cout << std::endl; + + std::cout << dynamic_array_queue_3.head() << std::endl; + dynamic_array_queue_3.dequeue(); + std::cout << dynamic_array_queue_3.head() << std::endl; + dynamic_array_queue_3.dequeue(); + std::cout << dynamic_array_queue_3.head() << std::endl; + dynamic_array_queue_3.dequeue(); + // std::cout << dynamic_array_queue_3.head() << std::endl; // throw + // dynamic_array_queue_3.dequeue(); // throw + + DynamicArrayQueue dynamic_array_queue_4(1); + dynamic_array_queue_4 = dynamic_array_queue_1; // copy assignment + dynamic_array_queue_4.traverse(do_traverse); + std::cout << std::endl; + + DynamicArrayQueue dynamic_array_queue_5(100); + dynamic_array_queue_5 = std::move(dynamic_array_queue_4); // move assignment + dynamic_array_queue_5.traverse(do_traverse); + std::cout << std::endl; + dynamic_array_queue_4.traverse(do_traverse); + std::cout << std::endl; + + std::cout << dynamic_array_queue_5.head() << std::endl; + dynamic_array_queue_5.dequeue(); + std::cout << dynamic_array_queue_5.head() << std::endl; + dynamic_array_queue_5.dequeue(); + std::cout << dynamic_array_queue_5.head() << std::endl; + dynamic_array_queue_5.dequeue(); + // std::cout << dynamic_array_queue_5.head() << std::endl; // throw + // dynamic_array_queue_5.dequeue(); // throw + + for (size_t i = 0; i != 3; ++i) { + dynamic_array_queue_1.dequeue(); + dynamic_array_queue_1.enqueue(i + 4); + dynamic_array_queue_1.traverse(do_traverse); + std::cout << std::endl; + } + return 0; +} diff --git a/c-cpp/09_queue/linked_queue.hpp b/c-cpp/09_queue/linked_queue.hpp new file mode 100644 index 00000000..13727753 --- /dev/null +++ b/c-cpp/09_queue/linked_queue.hpp @@ -0,0 +1,75 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/10. + */ + +#ifndef QUEUE_LINKED_QUEUE_HPP_ +#define QUEUE_LINKED_QUEUE_HPP_ + +#include + +template +struct Node { + using ptr_t = std::shared_ptr>; + T data; + ptr_t next; + + Node(T data_) : data(data_), next(nullptr) {} + Node() : next(nullptr) {} +}; + +template +class LinkedQueue { + public: + using node_type = Node; + using node_ptr_t = typename node_type::ptr_t; + + private: + node_ptr_t head_ = nullptr; + node_ptr_t before_tail_ = nullptr; + + public: + LinkedQueue() = default; + ~LinkedQueue() = default; + LinkedQueue(const LinkedQueue& other) = default; + LinkedQueue& operator=(const LinkedQueue& rhs) = default; + LinkedQueue(LinkedQueue&& other) = default; + LinkedQueue& operator=(LinkedQueue&& rhs) = default; + + public: + void enqueue(T item) { + if (nullptr == head_) { + head_ = std::make_shared(item); + before_tail_ = head_; + } else { + before_tail_->next = std::make_shared(item); + before_tail_ = before_tail_->next; + } + } + T head() const { + if (nullptr != head_) { + return head_->data; + } else { + throw "Fetch data from an empty queue!"; + } + } + void dequeue() { + if (nullptr != head_) { + head_ = head_->next; + if (nullptr == head_) { + before_tail_ = nullptr; + } + } else { + throw "Pop data from an empty queue!"; + } + } + + public: + template + void traverse(UnaryFunc do_traverse) { + for (node_ptr_t work = head_; nullptr != work; work = work->next) { + do_traverse(work->data); + } + } +}; + +#endif // QUEUE_LINKED_QUEUE_HPP_ diff --git a/c-cpp/09_queue/linked_queue_test.cc b/c-cpp/09_queue/linked_queue_test.cc new file mode 100644 index 00000000..04a90195 --- /dev/null +++ b/c-cpp/09_queue/linked_queue_test.cc @@ -0,0 +1,55 @@ +#include +#include "linked_queue.hpp" + +int main() { + auto do_traverse = [&](auto item){ std::cout << item << ' '; }; + + LinkedQueue linked_queue_1; + linked_queue_1.enqueue(1); + linked_queue_1.enqueue(2); + linked_queue_1.enqueue(3); + linked_queue_1.traverse(do_traverse); + std::cout << std::endl; + + LinkedQueue linked_queue_2(linked_queue_1); // copy constructor + linked_queue_2.traverse(do_traverse); + std::cout << std::endl; + + LinkedQueue linked_queue_3(std::move(linked_queue_2)); // move constructor + linked_queue_3.traverse(do_traverse); + std::cout << std::endl; + linked_queue_2.traverse(do_traverse); + std::cout << std::endl; + + std::cout << linked_queue_3.head() << std::endl; + linked_queue_3.dequeue(); + std::cout << linked_queue_3.head() << std::endl; + linked_queue_3.dequeue(); + std::cout << linked_queue_3.head() << std::endl; + linked_queue_3.dequeue(); + // std::cout << linked_queue_3.head() << std::endl; // throw + // linked_queue_3.dequeue(); // throw + + LinkedQueue linked_queue_4; + linked_queue_4 = linked_queue_1; // copy assignment + linked_queue_4.traverse(do_traverse); + std::cout << std::endl; + + LinkedQueue linked_queue_5; + linked_queue_5 = std::move(linked_queue_4); // move assignment + linked_queue_5.traverse(do_traverse); + std::cout << std::endl; + linked_queue_4.traverse(do_traverse); + std::cout << std::endl; + + std::cout << linked_queue_5.head() << std::endl; + linked_queue_5.dequeue(); + std::cout << linked_queue_5.head() << std::endl; + linked_queue_5.dequeue(); + std::cout << linked_queue_5.head() << std::endl; + linked_queue_5.dequeue(); + // std::cout << linked_queue_5.head() << std::endl; // throw + // linked_queue_5.dequeue(); // throw + + return 0; +} diff --git a/c-cpp/09_queue/list_queue/list_queue.c b/c-cpp/09_queue/list_queue/list_queue.c new file mode 100644 index 00000000..ea78bb66 --- /dev/null +++ b/c-cpp/09_queue/list_queue/list_queue.c @@ -0,0 +1,171 @@ +/************************************************************************* + > File Name: list_queue.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-13 + > Desc: + ************************************************************************/ +#include +#include +#include +#include"./list_queue.h" + +/*创建队列头*/ +list_queue *list_queue_create() +{ + list_queue * queue = NULL; + + queue = (list_queue *)malloc(sizeof(list_queue)); + if(queue == NULL) + { + return NULL; + } + + queue->num = 0; + queue->head = NULL; + queue->tail = NULL; + + return queue; +} +void list_queue_destroy(list_queue*queue) +{ + int i = 0; + int data = 0; + + if ((queue == NULL) || (list_queue_is_empty(queue))) + { + return ; + } + + while(!list_queue_is_empty(queue)) + { + (void)list_queue_dequeue(queue,&data); + } + + free(queue); + return; +} +int list_queue_enqueue(list_queue *queue,int data) +{ + queue_node *ptmp = NULL; + + if(queue == NULL) + { + return -1; + } + + ptmp = (queue_node *)malloc(sizeof(queue_node)); + if (ptmp == NULL) + { + return -1; + } + + ptmp->data = data; + ptmp->next = NULL; + if (queue->head == NULL) + { + queue->head = ptmp; + } + else + { + queue->tail->next = ptmp; + + } + queue->tail = ptmp; + queue->num++; + + return 0; +} + +/*出队*/ +int list_queue_dequeue(list_queue *queue,int *data) +{ + queue_node * ptmp = NULL; + + if ((queue == NULL) || (data == NULL) || list_queue_is_empty(queue)) + { + return -1; + } + + *data = queue->head->data; + ptmp = queue->head; + queue->head = queue->head->next; + queue->num--; + + if (queue->head == NULL) + { + queue->tail = NULL; + } + + + free(ptmp); + return 0; +} +void list_queue_dump(list_queue*queue) +{ + int i = 0; + queue_node *ptmp = NULL; + + if ((queue == NULL) || (list_queue_is_empty(queue))) + { + return; + } + + ptmp = queue->head; + + printf("\r\n----dump queue num = %d--------",queue->num); + while(ptmp != NULL) + { + printf("\r\nnode[%d] = %d",i,ptmp->data); + i++; + ptmp = ptmp->next; + } + printf("\r\n---------------------------------\r\n"); + + return; +} + +int main() +{ + int i = 0; + int data = 0; + int ret = 0; + list_queue * queue; + + queue = list_queue_create(); + if (queue == NULL) + { + printf("\r\nlist queue create falied.."); + return 0; + } + + for (i = 0; i < 5; i++) + { + (void)list_queue_enqueue(queue,i); + } + list_queue_dump(queue); + + ret = list_queue_dequeue(queue,&data); + if(ret != 0) + { + printf("\r\nlist queue dequeue %d falied.",data); + } + printf("\r\nlist queue dequeue %d",data); + list_queue_dump(queue); + + + ret = list_queue_dequeue(queue,&data); + if(ret != 0) + { + printf("\r\nlist queue dequeue %d failed.",data); + } + printf("\r\nlist queue dequeue %d",data); + list_queue_dump(queue); + + printf("\r\nlist queue enqueue %d",data); + (void)list_queue_enqueue(queue,data); + list_queue_dump(queue); + + list_queue_destroy(queue); + return 0; +} diff --git a/c-cpp/09_queue/list_queue/list_queue.h b/c-cpp/09_queue/list_queue/list_queue.h new file mode 100644 index 00000000..b50ed2d8 --- /dev/null +++ b/c-cpp/09_queue/list_queue/list_queue.h @@ -0,0 +1,27 @@ +/************************************************************************* + > File Name: list_queue.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-13 + > Desc: + ************************************************************************/ + +#ifndef LINK_LIST_QUEUE_H +#define LINK_LIST_QUEUE_H + +typedef struct _list_queue_node +{ + int data; + struct _list_queue_node *next; +}queue_node; + +typedef struct _list_queue +{ + int num; + queue_node *head; + queue_node *tail; +}list_queue; + +#define list_queue_is_empty(queue) ((queue->num) == 0) + +#endif diff --git a/c-cpp/09_queue/lock_free_queue.hpp b/c-cpp/09_queue/lock_free_queue.hpp new file mode 100644 index 00000000..3f02d8ae --- /dev/null +++ b/c-cpp/09_queue/lock_free_queue.hpp @@ -0,0 +1,84 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/11. + */ + +#ifndef QUEUE_LOCK_FREE_QUEUE_HPP_ +#define QUEUE_LOCK_FREE_QUEUE_HPP_ + +#include +#include + +template +class LockFreeQueue { + public: + using value_type = T; + + private: + struct node { + std::shared data = nullptr; + node* next = nullptr; + }; + std::atomic head = nullptr; + std::atomic tail = nullptr; + + public: + LockFreeQueue() head(new node), tail(head.load()) {} + LockFreeQueue(const LockFreeQueue&) = delete; + LockFreeQueue(LockFreeQueue&& other) : head(other.head.load()), tail(other.tail.load()) { + other.head.store(nullptr); + other.tail.store(nullptr); + } + LockFreeQueue& operator=(const LockFreeQueue&) = delete; + LockFreeQueue& operator=(LockFreeQueue&& rhs) { + while (node* const old_head = head.load()) { + head.store(old_head->next); + delete old_head; + } + head.store(rhs.head.load()); + tail.store(rhs.tail.load()); + rhs.head.store(nullptr); + rhs.tail.store(nullptr); + } + ~LockFreeQueue() { + while (node* const old_head = head.load()) { + head.store(old_head->next); + delete old_head; + } + } + + private: + node* pop_head() { + node* const res = head.load(); + if (res == tail.load()) { + return nullptr; + } + head.store(res->next); + return res; + } + + public: + bool empty() const { + return head.load() == tail.load(); + } + std::shared_ptr pop() { + node* old_head = pop_head(); + if (nullptr == old_head) { + return nullptr; + } else { + auto res = old_head->data; + delete old_head; + return res; + } + } + void push(value_type new_value) { + auto new_data = std::make_shared(new_value); + node* p = new node; + node* old_tail = tail.load(); + old_tail->data.swap(new_data); + old_tail->next = p; + tail_.store(p); + } +}; + +#endif // QUEUE_LOCK_FREE_QUEUE_HPP_ + diff --git a/c-cpp/10_recursive/.gitkeep b/c-cpp/10_recursive/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/10_recursive/one_two_step.c b/c-cpp/10_recursive/one_two_step.c new file mode 100644 index 00000000..02d503e3 --- /dev/null +++ b/c-cpp/10_recursive/one_two_step.c @@ -0,0 +1,57 @@ +/************************************************************************* + > File Name: one_two_step.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-19 + > Desc: + ************************************************************************/ +#include +#include +#include + +/*爬楼梯的问题,解决重复计算,采用数据保存方法*/ + +int helper(int n ,int *vlaue) +{ + + if(vlaue[n] != 0) + { + return vlaue[n]; + } + + vlaue[n] = helper(n - 1,vlaue) + helper(n - 2,vlaue); + + return vlaue[n]; +} + +int climbStaris(int n) +{ + int *vlaue = NULL; + int res = 0; + + vlaue = (int *)malloc(sizeof(int)*(n+1)); + if(vlaue == NULL) + { + return -1; + } + + memset(vlaue,0,sizeof(int)*(n + 1)); + vlaue[0] = 0; + vlaue[1] = 1; + vlaue[2] = 2; + res = helper(n,vlaue); + free(vlaue); + + return res; +} + +int main() +{ + + printf("\r\nnum%d ,%d",5,climbStaris(5)); + printf("\r\nnum%d ,%d",6,climbStaris(6)); + printf("\r\nnum%d ,%d",7,climbStaris(7)); + return 0; +} + + diff --git a/c-cpp/10_recursive/one_two_step.cc b/c-cpp/10_recursive/one_two_step.cc new file mode 100644 index 00000000..11159c7f --- /dev/null +++ b/c-cpp/10_recursive/one_two_step.cc @@ -0,0 +1,71 @@ +#include +#include + +class SolutionOFOneTwoStep { + private: + static std::unordered_map result_; + + public: + enum class POLICY { + RECURSIVE, + NONRECURSIVE + }; + + private: + size_t recursive(size_t steps) { + auto iter = result_.find(steps); + if (result_.end() != iter) { // found. + return iter->second; + } else { + size_t res = operator()(steps - 1) + operator()(steps - 2); + result_.insert({steps, res}); + return res; + } + } + size_t nonrecursive(size_t steps) { + auto iter = result_.find(steps); + if (result_.end() != iter) { // found. + return iter->second; + } else { + size_t start; + for (start = steps; start != 2 and result_.end() == result_.find(start); --start) {} + for (size_t i = start; i != steps; ++i) { + result_.insert({i + 1, result_[i - 1] + result_[i]}); + } + return result_[steps]; + } + } + + public: + size_t operator()(size_t steps, const POLICY policy = POLICY::RECURSIVE) { + if (policy == POLICY::RECURSIVE) { + return recursive(steps); + } else if (policy == POLICY::NONRECURSIVE) { + return nonrecursive(steps); + } + } + static void debug() { + for (auto kv : result_) { + std::cout << kv.first << ' ' << kv.second << std::endl; + } + std::cout << std::endl; + } +}; + +std::unordered_map SolutionOFOneTwoStep::result_ = {{1, 1}, {2, 2}}; + +int main() { + SolutionOFOneTwoStep::debug(); + + std::cout << SolutionOFOneTwoStep()(5, SolutionOFOneTwoStep::POLICY::RECURSIVE) << std::endl; + SolutionOFOneTwoStep::debug(); + + std::cout << SolutionOFOneTwoStep()(10, SolutionOFOneTwoStep::POLICY::NONRECURSIVE) << std::endl; + SolutionOFOneTwoStep::debug(); + + std::cout << SolutionOFOneTwoStep()(20, SolutionOFOneTwoStep::POLICY::RECURSIVE) << std::endl; + SolutionOFOneTwoStep::debug(); + + return 0; +} + diff --git a/c-cpp/11_sorts/.gitkeep b/c-cpp/11_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/11_sorts/sorts.c b/c-cpp/11_sorts/sorts.c new file mode 100644 index 00000000..f759077a --- /dev/null +++ b/c-cpp/11_sorts/sorts.c @@ -0,0 +1,139 @@ +#include +#include +#include + +struct array { + int size; + int used; + int *arr; +}; + +void dump(struct array *array) +{ + int idx; + + for (idx = 0; idx < array->used; idx++) + printf("[%02d]: %08d\n", idx, array->arr[idx]); +} + +void alloc(struct array *array) +{ + array->arr = (int *)malloc(array->size * sizeof(int)); +} + +void bubble_sort(struct array *array) +{ + int i, j; + + if (array->used <= 1) + return; + + for (i = 0; i < array->used; i++) { + bool has_swap = false; + for (j = 0; j < array->used - i - 1; j++) { + if (array->arr[j] > array->arr[j+1]) { + int tmp; + tmp = array->arr[j]; + array->arr[j] = array->arr[j+1]; + array->arr[j+1] = tmp; + has_swap = true; + } + + } + if (!has_swap) + break; + } +} + +void bubble_sort_test() +{ + int idx; + struct array ten_int = {10, 0, NULL}; + + alloc(&ten_int); + for (idx = 0; idx < 10; idx++) + ten_int.arr[idx] = 30 - idx; + ten_int.used = 10; + dump(&ten_int); + bubble_sort(&ten_int); + dump(&ten_int); +} + +void insertion_sort(struct array *array) +{ + int i, j; + + if (array->used <= 1) + return; + + for (i = 1; i < array->used; i++) { + int val = array->arr[i]; + + for (j = i - 1; j >= 0; j--) { + if (val < array->arr[j]) + array->arr[j+1] = array->arr[j]; + else + break; + } + array->arr[j+1] = val; + } +} + +void insertion_sort_test() +{ + int idx; + struct array ten_int = {10, 0, NULL}; + + alloc(&ten_int); + for (idx = 0; idx < 10; idx++) + ten_int.arr[idx] = 30 - idx; + ten_int.used = 10; + dump(&ten_int); + insertion_sort(&ten_int); + dump(&ten_int); +} + +void selection_sort(struct array *array) +{ + int i, j; + + if (array->used <= 1) + return; + + for (i = 0; i < array->used - 1; i++) { + int tmp, idx = i; + + for (j = i + 1; j < array->used; j++) + if (array->arr[j] < array->arr[idx]) + idx = j; + + if (idx == i) + continue; + + tmp = array->arr[i]; + array->arr[i] = array->arr[idx]; + array->arr[idx] = tmp; + } +} + +void selection_sort_test() +{ + int idx; + struct array ten_int = {10, 0, NULL}; + + alloc(&ten_int); + for (idx = 0; idx < 10; idx++) + ten_int.arr[idx] = 30 - idx; + ten_int.used = 10; + dump(&ten_int); + selection_sort(&ten_int); + dump(&ten_int); +} + +int main() +{ + //bubble_sort_test(); + //selection_sort_test(); + insertion_sort_test(); + return 0; +} diff --git a/c-cpp/11_sorts/sorts.cpp b/c-cpp/11_sorts/sorts.cpp new file mode 100644 index 00000000..1adf676a --- /dev/null +++ b/c-cpp/11_sorts/sorts.cpp @@ -0,0 +1,47 @@ +// C program for implementation of selection sort +#include + +void swap(int *xp, int *yp) +{ + int temp = *xp; + *xp = *yp; + *yp = temp; +} + +void selectionSort(int arr[], int n) +{ + int i, j, min_idx; + + // One by one move boundary of unsorted subarray + for (i = 0; i < n-1; i++) + { + // Find the minimum element in unsorted array + min_idx = i; + for (j = i+1; j < n; j++) + if (arr[j] < arr[min_idx]) + min_idx = j; + + // Swap the found minimum element with the first element + swap(&arr[min_idx], &arr[i]); + } +} + +/* Function to print an array */ +void printArray(int arr[], int size) +{ + int i; + for (i=0; i < size; i++) + printf("%d ", arr[i]); + printf("\n"); +} + +// Driver program to test above functions +int main() +{ + int arr[] = {64, 25, 12, 22, 11}; + int n = sizeof(arr)/sizeof(arr[0]); + selectionSort(arr, n); + printf("Sorted array: \n"); + printArray(arr, n); + return 0; +} diff --git a/c-cpp/11_sorts/sorts.hpp b/c-cpp/11_sorts/sorts.hpp new file mode 100644 index 00000000..eb272c41 --- /dev/null +++ b/c-cpp/11_sorts/sorts.hpp @@ -0,0 +1,89 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/16. + */ + +#ifndef SORTS_SORTS_HPP_ +#define SORTS_SORTS_HPP_ + +#include +#include + +template ::value_type>> +void bubble_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { + if (std::distance(first, last) <= 1) { return; } + bool flag = true; + for (auto it = first; flag and it != last; ++it) { + flag = false; + for (auto itt = first; itt != last - std::distance(first, it) - 1; ++itt) { + if (comp(*(itt + 1), *itt)) { + std::swap(*itt, *(itt + 1)); + flag = true; + } + } + } +} + +template ::value_type>> +void insertion_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { + if (std::distance(first, last) <= 1) { return; } + for (auto it = first + 1; it != last; ++it) { + const auto target = *it; + auto itt = it; + for (; std::distance(first, itt) > 0 and comp(target, *(itt - 1)); --itt) { + *itt = *(itt - 1); + } + *itt = target; + } +} + +template ::value_type>> +void selection_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { + if (std::distance(first, last) <= 1) { return; } + for (auto it = first; it != last - 1; ++it) { + auto tag = it; + for (auto itt = it + 1; itt != last; ++itt) { + if (comp(*itt, *tag)) { + tag = itt; + } + } + if (tag != it) { + std::swap(*it, *tag); + } + } +} + +template ::value_type>> +void bubble_down_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) { + if (std::distance(first, last) <= 1) { return; } + for (auto it = first; it != last; ++it) { + for (auto itt = it + 1; itt != last; ++itt) { + if (comp(*itt, *it)) { + std::swap(*it, *itt); + } + } + } +} + +template ::value_type>> +void shell_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { + const size_t len = std::distance(first, last); + if (len <= 1) { return; } + for (size_t step = len / 2; step >= 1; step /= 2) { + for (auto it = first + step; it != last; ++it) { + auto target = *it; + auto itt = it - step; + for (; std::distance(first, itt) >= 0 and comp(target, *itt); itt -= step) { + *(itt + step) = *itt; + } + *(itt + step) = target; + } + } +} + +#endif // SORTS_SORTS_HPP_ + diff --git a/c-cpp/11_sorts/sorts_jinshaohui.c b/c-cpp/11_sorts/sorts_jinshaohui.c new file mode 100644 index 00000000..9e5675f0 --- /dev/null +++ b/c-cpp/11_sorts/sorts_jinshaohui.c @@ -0,0 +1,124 @@ +/************************************************************************* + > File Name: sorts_jinshaohui.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-19 + > Desc: + ************************************************************************/ +#include +#include +#include + +#define SWAP(a,b) \ +do{\ + (a) ^= (b);\ + (b) ^= (a);\ + (a) ^= (b);\ +}while(0) + +/*冒泡排序*/ +void bubble_sort(int a[],int size) +{ + int i = 0; + int j = 0; + int swap_flg = 0; + + if (size < 1) + { + return; + } + + for (i = size - 1; i > 0; i--)/*排序的趟数*/ + { + swap_flg = 0;/*每次设置交换标识为0*/ + for (j = 0; j < i; j++)/*本趟排序的遍历元素个数*/ + { + if (a[j] > a[j + 1]) + { + SWAP(a[j],a[j+1]); + swap_flg = 1; + } + } + /*本趟数,无数据交换的话,说明已经有序,直接退出*/ + if (swap_flg == 0) + { + break; + } + } + return; +} + +/*插入排序*/ +void insert_sort(int a[],int size) +{ + int i = 0; + int j = 0; + int key = 0; + + for (i = 1; i < size; i ++)/*需要插入的元素个数*/ + { + key = a[i];/*保存插入的元素数据*/ + j = i - 1; + /* i 之前的元素都是有序的,找到比key小的插入到他后面, + * 比key大的,需要往后挪一个位置*/ + while((j >= 0) && (a[j] > key)) + { + a[j + 1] = a[j]; + j--; + } + a[j + 1] = key; + } + + return; +} +/*选择排序*/ +void select_sort(int a[],int size) +{ + int i = 0; + int j = 0; + int min = 0; + + for (i = 0; i < size - 1; i++) + { + min = i; + for (j = i + 1; j < size; j++) + { + if (a[j] < a[min]) + { + min = j; + } + } + + if (min != i) + { + SWAP(a[i],a[min]); + } + } + return; +} + +void dump(int a[],int size) +{ + int i = 0; + + printf("\r\n"); + for (i = 0; i < size; i++ ) + { + printf("%d ",a[i]); + } + printf("\r\n"); + return; +} + +int main() +{ + int a[10] = {9,11,4,15,16,3,20,44,5,10}; + + //bubble_sort(a,sizeof(a)/sizeof(int)); + //insert_sort(a,sizeof(a)/sizeof(int)); + select_sort(a,sizeof(a)/sizeof(int)); + + dump(a,sizeof(a)/sizeof(int)); + + return 0; +} diff --git a/c-cpp/11_sorts/sorts_test.cc b/c-cpp/11_sorts/sorts_test.cc new file mode 100644 index 00000000..723942b0 --- /dev/null +++ b/c-cpp/11_sorts/sorts_test.cc @@ -0,0 +1,45 @@ +#include +#include + +#include "sorts.hpp" + +int main() { + const std::vector test_data{1, 2, 3, 0}; + + std::vector a(test_data.begin(), test_data.end()); + bubble_sort(a.begin(), a.end()); + for (auto i : a) { + std::cout << i << ' '; + } + std::cout << '\n'; + + std::vector b(test_data.begin(), test_data.end()); + insertion_sort(b.begin(), b.end()); + for (auto i : b) { + std::cout << i << ' '; + } + std::cout << '\n'; + + std::vector c(test_data.begin(), test_data.end()); + selection_sort(c.begin(), c.end()); + for (auto i : c) { + std::cout << i << ' '; + } + std::cout << '\n'; + + std::vector d(test_data.begin(), test_data.end()); + bubble_down_sort(d.begin(), d.end()); + for (auto i : d) { + std::cout << i << ' '; + } + std::cout << '\n'; + + std::vector e(test_data.begin(), test_data.end()); + shell_sort(e.begin(), e.end()); + for (auto i : e) { + std::cout << i << ' '; + } + std::cout << '\n'; + + return 0; +} diff --git a/c-cpp/12_sorts/.gitkeep b/c-cpp/12_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/12_sorts/merge_sort.c b/c-cpp/12_sorts/merge_sort.c new file mode 100644 index 00000000..9e626662 --- /dev/null +++ b/c-cpp/12_sorts/merge_sort.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include + +void dump(int *arr, int size) +{ + int idx; + + for (idx = 0; idx < size; idx++) + printf("%08d\n", arr[idx]); +} + +void __merge(int *arr, int p, int q, int r) +{ + int *tmp; + int i, j, k; + + tmp = (int*)malloc((r - p + 1) * sizeof(int)); + + if (!tmp) + abort(); + + for (i = p, j = q + 1, k = 0; i <= q && j <= r;) { + if (arr[i] <= arr[j]) + tmp[k++] = arr[i++]; + else + tmp[k++] = arr[j++]; + } + + if (i == q + 1) { + for (; j <= r;) + tmp[k++] = arr[j++]; + } else { + for (; i <= q;) + tmp[k++] = arr[i++]; + } + + memcpy(arr + p, tmp, (r - p + 1) * sizeof(int)); + free(tmp); +} + +void __merge_sort(int *arr, int p, int r) +{ + int q; + + if (p >= r) + return; + + q = (p + r) / 2; + __merge_sort(arr, p, q); + __merge_sort(arr, q + 1, r); + __merge(arr, p, q, r); +} + +void merge_sort(int *arr, int size) +{ + __merge_sort(arr, 0, size - 1); +} + +void merge_verify() +{ + int test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56}; + + __merge(test, 0, 4, 9); + + dump(test, 10); +} + +void merge_sort_test() +{ + int test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56}; + + merge_sort(test, 10); + + dump(test, 10); +} + +int main() +{ + //merge_verify(); + merge_sort_test(); + return 0; +} diff --git a/c-cpp/12_sorts/merge_sort.hpp b/c-cpp/12_sorts/merge_sort.hpp new file mode 100644 index 00000000..aa604685 --- /dev/null +++ b/c-cpp/12_sorts/merge_sort.hpp @@ -0,0 +1,62 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/17. + */ + +#ifndef SORTS_MERGE_SORT_HPP_ +#define SORTS_MERGE_SORT_HPP_ + +#include +#include +#include +#include + +namespace detail { +template ::value_type>> +OutputIt merge(InputIt1 first1, InputIt1 last1, + InputIt2 first2, InputIt2 last2, + OutputIt d_first, + BinaryPred comp = BinaryPred()) { + for (; first1 != last1; ++d_first) { + if (first2 == last2) { + return std::copy(first1, last1, d_first); + } + if (comp(*first2, *first1)) { + *d_first = *first2; + ++first2; + } else { + *d_first = *first1; + ++first1; + } + } + return std::copy(first2, last2, d_first); +} +} // namespace detail + +template ::value_type>> +void merge_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) { + const auto len = std::distance(first, last); + if (len <= 1) { return; } + auto cut = first + len / 2; + merge_sort(first, cut, comp); + merge_sort(cut, last, comp); + std::vector::value_type> tmp; + tmp.reserve(len); + detail::merge(first, cut, cut, last, std::back_inserter(tmp), comp); + std::copy(tmp.begin(), tmp.end(), first); +} + +template ::value_type>> +void inplace_merge_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) { + const auto len = std::distance(first, last); + if (len <= 1) { return; } + auto cut = first + len / 2; + inplace_merge_sort(first, cut, comp); + inplace_merge_sort(cut, last, comp); + std::inplace_merge(first, cut, last, comp); +} + +#endif // SORTS_MERGE_SORT_HPP_ + diff --git a/c-cpp/12_sorts/merge_sort_test.cc b/c-cpp/12_sorts/merge_sort_test.cc new file mode 100644 index 00000000..35bc8a6c --- /dev/null +++ b/c-cpp/12_sorts/merge_sort_test.cc @@ -0,0 +1,28 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/17. + */ + +#include +#include +#include "merge_sort.hpp" + +int main() { + const std::vector test_data{0, -1, 3, 190, -500}; + + std::vector a{test_data}; + merge_sort(a.begin(), a.end()); + for (auto i : a) { + std::cout << i << ' '; + } + std::cout << std::endl; + + std::vector b{test_data}; + inplace_merge_sort(b.begin(), b.end()); + for (auto i : b) { + std::cout << i << ' '; + } + std::cout << std::endl; + + return 0; +} + diff --git a/c-cpp/12_sorts/my12_sorts/merge_sort.c b/c-cpp/12_sorts/my12_sorts/merge_sort.c new file mode 100644 index 00000000..58643514 --- /dev/null +++ b/c-cpp/12_sorts/my12_sorts/merge_sort.c @@ -0,0 +1,160 @@ +/************************************************************************* + > File Name: merge_sort.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-19 + > Desc: + ************************************************************************/ +#include +#include +#include +#include + +#define SORT_MAX (1000000) + +void dump(int a[],int size); +void merge_sentry(int a[],int middle,int left,int right) +{ + int *pleft = NULL; + int *pright = NULL; + int i = 0; + int j = 0; + int k = 0; + int left_size = middle - left + 1; + int right_size = right - middle; + + + pleft = (int *)malloc(sizeof(int)*(left_size + 1)); + assert(pleft != NULL); + pright = (int *)malloc(sizeof(int)*(right_size + 1)); + assert(pright != NULL); + + for(i = 0; i < left_size; i ++) + { + pleft[i] = a[left + i]; + } + pleft[left_size] = SORT_MAX; + for(i = 0; i < right_size; i ++) + { + pright[i] = a[middle + 1 + i]; + } + pright[right_size] = SORT_MAX; + + for (k = left,i = 0,j = 0; k <= right; k++) + { + if (pleft[i] <= pright[j]) + { + a[k] = pleft[i++]; + } + else + { + a[k] = pright[j++]; + } + } + + free(pleft); + free(pright); + + return; +} + +/*两个有序数组合并*/ +void merge(int a[],int middle,int left,int right) +{ + int *tmp = NULL; + int i = 0; + int j = 0; + int k = 0; + + tmp = (int*)malloc((right - left + 1)*sizeof(int)); + assert(tmp != NULL); + + i = left; + j = middle + 1; + + while(1) + { + if((i > middle) || (j > right)) + { + break; + } + + if (a[i] > a[j]) + { + tmp[k++] = a[j++]; + } + else + { + tmp[k++] = a[i++]; + } + } + + if (i > middle) + { + while(j <= right) + { + tmp[k++] = a[j++]; + } + } + else + { + while(i <= middle) + { + tmp[k++] = a[i++]; + } + } + + memcpy((a + left),tmp,(right - left + 1)*sizeof(int)); + + free(tmp); + + return ; +} + +void merge_sort(int a[],int left,int right) +{ + int middle = 0; + + if(left >= right) + { + return; + } + + middle = (left + right)/2; + + merge_sort(a,left,middle); + merge_sort(a,middle + 1,right); + + merge_sentry(a,middle,left,right); + + return; +} + +void dump(int a[],int size) +{ + int i = 0; + + if(size == 0) + { + return; + } + + printf("\r\n"); + for (i = 0; i < size; i++ ) + { + printf("%d ",a[i]); + } + printf("\r\n"); + return; +} +int main() +{ + int a[10] = {30,20,10,15,4,8,40,80,20,9}; + + merge_sort(a,0,9); + + dump(a,10); + + return 0; +} + diff --git a/c-cpp/12_sorts/my12_sorts/quick_sort.c b/c-cpp/12_sorts/my12_sorts/quick_sort.c new file mode 100644 index 00000000..54ec480d --- /dev/null +++ b/c-cpp/12_sorts/my12_sorts/quick_sort.c @@ -0,0 +1,145 @@ +/************************************************************************* + > File Name: quick_sort.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-19 + > Desc: + ************************************************************************/ +#include +#include +#include +#include + +/* SWAP 使用必须主要,不能是同一个数据进行交换*/ +#define SWAP(a,b) \ +do{\ + (a) ^= (b);\ + (b) ^= (a);\ + (a) ^= (b);\ +}while(0) + +int partition2(int a[],int left,int right) +{ + int i = left; + int j = left; + + for(; j < right;j++) + { + if (a[j] < a[right]) + { + if(i != j) + { + SWAP(a[i],a[j]); + } + i++; + } + } + + if(i != right) + { + SWAP(a[i],a[right]); + } + + return i; +} + +int partition(int a[],int left,int right) +{ + int i = left; + int j = right; + int key = a[left]; + + while(i < j) + { + while((i < j)&& (a[j] >= key)) + { + j--; + } + if (i < j) + { + a[i] = a[j]; + } + while((i < j) && a[i] <= key) + { + i++; + } + + if (i= right) + { + return; + } + + q = partition2(a,left,right); + quick_sort(a,left,(q - 1)); + quick_sort(a,(q + 1),right); + return; +} + +void dump(int a[],int size) +{ + int i = 0; + + printf("\r\n"); + for (i = 0; i < size; i++ ) + { + printf("%d ",a[i]); + } + printf("\r\n"); + return; +} + +int helper(int a[],int left,int right,int k) +{ + int q = 0; + + q = partition(a,left,right); + + if (q > (k - 1)) + { + return helper(a,left,q-1,k); + } + else if (q < (k - 1)) + { + return helper(a,q+1,right,k); + } + + return a[q]; +} + +/*求无序数组中从小到大第K个元素的数值*/ +int findKthlagest(int a[],int size,int k) +{ + return helper(a,0,size-1,k); +} + + +int main() +{ + int a[10] = {30,20,10,15,4,8,40,80,20,9}; + int k; + + scanf("%d",&k); + + printf("\r\n从小到大排序,第%d个元素数值是%d",k,findKthlagest(a,10,k)); + + quick_sort(a,0,9); + + dump(a,10); + + return 0; +} + diff --git a/c-cpp/12_sorts/quick_sort.c b/c-cpp/12_sorts/quick_sort.c new file mode 100644 index 00000000..067d28f6 --- /dev/null +++ b/c-cpp/12_sorts/quick_sort.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +void dump(int *arr, int size) +{ + int idx; + + for (idx = 0; idx < size; idx++) + printf("%08d\n", arr[idx]); +} + +void swap(int *a, int *b) +{ + int tmp = *a; + *a = *b; + *b = tmp; +} + +int partition(int *arr, int p, int r) +{ + //int pivot = arr[r]; + int i, j; + + i = j = p; + + for (; j < r; j++) { + if (arr[j] < arr[r]) { + if(i != j) + { + swap(arr + i, arr + j); + + } + i++; + } + } + + swap(arr + i, arr + r); + return i; +} + +void __quick_sort(int *arr, int p, int r) +{ + int q; + + if (p >= r) + return; + + q = partition(arr, p, r); + __quick_sort(arr, p, q-1); + __quick_sort(arr, q+1, r); +} + +void quick_sort(int *arr, int size) +{ + __quick_sort(arr, 0, size - 1); +} + +void quick_sort_test() +{ + int test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56}; + + quick_sort(test, 10); + + dump(test, 10); +} + +int main() +{ + quick_sort_test(); + return 0; +} diff --git a/c-cpp/12_sorts/quick_sort.hpp b/c-cpp/12_sorts/quick_sort.hpp new file mode 100644 index 00000000..e0c385a0 --- /dev/null +++ b/c-cpp/12_sorts/quick_sort.hpp @@ -0,0 +1,71 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/17. + */ + +#ifndef SORTS_QUICK_SORT_HPP_ +#define SORTS_QUICK_SORT_HPP_ + +#include +#include +#include +#include + +namespace detail { +template > +const T& median(const T& a, const T& b, const T& c, Compare comp = Compare()) { + if (comp(a, b) and comp(b, c) or comp(c, b) and comp(b, a)) { + return b; + } else if (comp(b, c) and comp(c, a) or comp(a, c) and comp(c, b)) { + return c; + } else { + return a; + } +} + +template ::value_type, + typename Compare = std::less> +const T& iter_median(Iter a, Iter b, Iter c, Compare comp = Compare()) { + return median(*a, *b, *c, comp); +} + +template ::value_type, + typename Compare = std::less> +std::pair inplace_partition(BidirIt first, + BidirIt last, + const T& pivot, + Compare comp = Compare()) { + BidirIt last_less, last_greater, first_equal, last_equal; + for (last_less = first, last_greater = first, first_equal = last; + last_greater != first_equal; ) { + if (comp(*last_greater, pivot)) { + std::iter_swap(last_greater++, last_less++); + } else if (comp(pivot, *last_greater)) { + ++last_greater; + } else { // pivot == *last_greater + std::iter_swap(last_greater, --first_equal); + } + } + const auto cnt = std::distance(first_equal, last); + std::swap_ranges(first_equal, last, last_less); + first_equal = last_less; + last_equal = first_equal + cnt; + return {first_equal, last_equal}; +} +} // namespace detail + +template ::value_type, + typename Compare = std::less> +void quick_sort(BidirIt first, BidirIt last, Compare comp = Compare()) { + for (auto size = std::distance(first, last); size > 1; size = std::distance(first, last)) { + const T pivot = detail::iter_median(first, last - 1, first + size / 2, comp); + const auto eq = detail::inplace_partition(first, last, pivot, comp); + quick_sort(first, eq.first, comp); + first = eq.second; // Liam Huang: economize half of recursive calling. + } +} + +#endif // SORTS_QUICK_SORT_HPP_ + diff --git a/c-cpp/12_sorts/quick_sort_test.cc b/c-cpp/12_sorts/quick_sort_test.cc new file mode 100644 index 00000000..742bdbb1 --- /dev/null +++ b/c-cpp/12_sorts/quick_sort_test.cc @@ -0,0 +1,25 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/17. + */ + +#include +#include +#include + +#include "quick_sort.hpp" + +void test_quick_sort(std::vector test_data) { + quick_sort(test_data.begin(), test_data.end()); + std::transform(test_data.begin(), test_data.end(), + std::ostream_iterator(std::cout, " "), [](int i){ return i; }); + std::cout << '\n'; +} + +int main() { + test_quick_sort({-3, -1, 1, -2, -3, 0, -3, 100, 1, 1, -100}); + test_quick_sort({1, 1, 1}); + test_quick_sort({1, 0, -1}); + test_quick_sort({1}); + return 0; +} + diff --git a/c-cpp/13_sorts/.gitkeep b/c-cpp/13_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/13_sorts/bucket_sort.hpp b/c-cpp/13_sorts/bucket_sort.hpp new file mode 100644 index 00000000..41133ff6 --- /dev/null +++ b/c-cpp/13_sorts/bucket_sort.hpp @@ -0,0 +1,43 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef SORTS_BUCKET_SORT_HPP_ +#define SORTS_BUCKET_SORT_HPP_ + +#include +#include +#include +#include + +template ::value_type, + typename Compare = std::less> +void bucket_sort(IterT first, IterT last, Compare comp = Compare()) { + const T min = *std::min_element(first, last), max = *std::max_element(first, last); + const T range = max + 1 - min; + const size_t bucket_num = (range - 1) / BucketSize + 1; + + std::vector> buckets(bucket_num); + for (auto b : buckets) { + b.reserve(2 * BucketSize); + } + + for (IterT i = first; i != last; ++i) { + size_t idx = (*i - min) / BucketSize; + buckets[idx].emplace_back(*i); + } + + IterT dest = first; + for (auto b : buckets) { + std::sort(b.begin(), b.end(), comp); + std::copy(b.begin(), b.end(), dest); + dest += b.size(); + } + + return; +} + +#endif // SORTS_BUCKET_SORT_HPP_ + diff --git a/c-cpp/13_sorts/bucket_sort_test.cc b/c-cpp/13_sorts/bucket_sort_test.cc new file mode 100644 index 00000000..3199e8ae --- /dev/null +++ b/c-cpp/13_sorts/bucket_sort_test.cc @@ -0,0 +1,33 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#include +#include +#include + +#include "bucket_sort.hpp" + +template > +void test_bucket_sort(Container cont, Compare comp = Compare()) { + bucket_sort(cont.begin(), cont.end(), comp); + std::transform(cont.begin(), cont.end(), std::ostream_iterator(std::cout, " "), + [](T i){ return i; }); + std::cout << std::endl; +} + +int main() { + std::vector test{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9}; + + test_bucket_sort<2>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<3>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<4>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<5>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + test_bucket_sort<6>(test); // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9 + + return 0; +} + diff --git a/c-cpp/13_sorts/counting_sort.hpp b/c-cpp/13_sorts/counting_sort.hpp new file mode 100644 index 00000000..5e7f7823 --- /dev/null +++ b/c-cpp/13_sorts/counting_sort.hpp @@ -0,0 +1,40 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef SORTS_COUNTING_SORT_HPP_ +#define SORTS_COUNTING_SORT_HPP_ + +#include +#include +#include +#include + +template ::value_type> +void counting_sort(IterT first, IterT last) { + const auto len = std::distance(first, last); + if (len < 2) { return; } + + const T max = *std::max_element(first, last); + if (max == 0) { return; } + + std::vector counter(max + 1); + for (IterT i = first; i != last; ++i) { + ++counter[*i]; + } + for (size_t i = 1; i != max + 1; ++i) { + const size_t j = max - i; + counter[j] += counter[j + 1]; // Liam Huang: count of numbers that is not less than j. + } + + std::vector temp(len); + for (IterT i = first; i != last; ++i) { + temp[len - counter[*i]] = *i; + --counter[*i]; // Liam Huang: stable for relative position. + } + std::copy(temp.begin(), temp.end(), first); +} + +#endif // SORTS_COUNTING_SORT_HPP_ + diff --git a/c-cpp/13_sorts/counting_sort_test.cc b/c-cpp/13_sorts/counting_sort_test.cc new file mode 100644 index 00000000..4e0814a6 --- /dev/null +++ b/c-cpp/13_sorts/counting_sort_test.cc @@ -0,0 +1,36 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#include +#include +#include + +#include "counting_sort.hpp" + +template +void test_counting_sort(Container cont) { + counting_sort(cont.begin(), cont.end()); + std::transform(cont.begin(), cont.end(), std::ostream_iterator(std::cout, " "), + [](T i){ return i; }); + std::cout << std::endl; +} + +int main() { + // Liam Huang: pi for test + const std::vector test1{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3}; + const std::vector test2{2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9}; + const std::vector test3{5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9}; + const std::vector test4{3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4}; + const std::vector test5{5, 9, 2, 3, 0, 7, 8, 1, 6, 4, 0, 6, 2, 8, 6}; + + test_counting_sort(test1); // 1 1 2 3 3 3 4 5 5 5 6 7 8 9 9 9 + test_counting_sort(test2); // 2 2 2 3 3 3 3 4 4 6 6 7 8 8 9 + test_counting_sort(test3); // 0 1 1 2 3 4 5 6 7 8 8 9 9 9 9 + test_counting_sort(test4); // 0 0 1 2 3 4 4 4 5 5 7 7 8 9 9 + test_counting_sort(test5); // 0 0 1 2 2 3 4 5 6 6 6 7 8 8 9 + + return 0; +} + diff --git a/c-cpp/13_sorts/sort.c b/c-cpp/13_sorts/sort.c new file mode 100755 index 00000000..50cb463d --- /dev/null +++ b/c-cpp/13_sorts/sort.c @@ -0,0 +1,268 @@ +/************************************************************************* + > File Name: sort.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-20 + > Desc: + ************************************************************************/ +#include +#include +#include +#include +#include + +void dump(int a[],int size) +{ + int i = 0; + + printf("\r\n"); + + for(i = 0; i max) + { + max = a[i]; + } + } + + count = (int *)malloc(sizeof(int)*(max + 1)); + assert(count != NULL); + + memset(count,0,sizeof(int)*(max + 1)); + + /*计数*/ + for (i = 0; i < size;i++) + { + count[a[i]]++; + } + + /*依次累加*/ + for(i = 1 ;i <= max; i ++) + { + count[i] += count[i-1]; + } + + res = (int *)malloc(sizeof(int)*(size)); + assert(res != NULL); + /*核心代码,count[a[i] - 1]就是排序好的下标*/ + for (i = size-1;i >= 0; i--) + { + res[count[a[i]] -1] = a[i]; + count[a[i]]--; + } + + memcpy(a,res,size*(sizeof(int))); + + free(res); + free(count); + return; +} + + +int count_sort_test() +{ + int a [10]={1,5,6,8,10,9,3,1,2,1}; + printf("\r\n conunt sort test ...."); + count_sort(a,10); + dump(a,10); + + return 0; +} + +#define NUM_OF_POS(a,pval) ((a)/pval)%10 +void radix_sort(int a[],int size,int num_count) +{ + int count[10] = {0}; /*计数*/ + int *pres = NULL; + int i = 0; + int j = 0; + int pval = 10; + int index = 0; + int break_flg = 0; + + pres = (int *)malloc(sizeof(int)*size); + assert(pres != NULL); + + for (i = 0; i < num_count; i ++) + { + memset(count,0,sizeof(int)*10); + + /*求当前的基数*/ + pval = pow(10,i); + + /*计数*/ + for (j = 0; j < size; j++) + { + index = NUM_OF_POS(a[j],pval); + count[index]++; + } + + /*小的优化,可能位数最大的就1,其他的位数差很多*/ + if(count[0] == 9) + { + break_flg++; + } + + if(break_flg >=2) + { + printf("\r\n %i",i); + break; + } + + /*累加*/ + for(j = 1; j < 10; j ++) + { + count[j] += count[j-1]; + } + + /*排序必须从后往前,否则不是稳定排序*/ + for(j = size -1; j >= 0; j--) + { + index = NUM_OF_POS(a[j],pval); + pres[count[index] - 1] = a[j]; + count[index]--; + } + /*本轮排序好的,拷贝到a中*/ + memcpy(a,pres,sizeof(int)*size); + } + + return; +} + +void radix_sort_test() +{ + int a[10] = {123,12341,1232134,124,236,128,1112313129,98,9,8989}; + printf("\r\n radix sort test....."); + radix_sort(a,10,10); + dump(a,10); + return; +} + +struct barrel { + int node[10]; + int count;/* the num of node */ +}; +int partition(int a[],int left,int right) +{ + int i = left; + int j = right; + int key = a[left]; + + while(i < j) + { + while((i < j)&& (a[j] >= key)) + { + j--; + } + if (i < j) + { + a[i] = a[j]; + } + while((i < j) && a[i] <= key) + { + i++; + } + + if (i= right) + { + return; + } + + q = partition(a,left,right); + quick_sort(a,left,(q - 1)); + quick_sort(a,(q + 1),right); + return; +} + +void bucket_sort(int data[], int size) +{ + int max, min, num, pos; + int i, j, k; + struct barrel *pBarrel; + + max = min = data[0]; + for (i = 1; i < size; i++) { + if (data[i] > max) { + max = data[i]; + } else if (data[i] < min) { + min = data[i]; + } + } + num = (max - min + 1) / 10 + 1; + pBarrel = (struct barrel*)malloc(sizeof(struct barrel) * num); + memset(pBarrel, 0, sizeof(struct barrel) * num); + + /* put data[i] into barrel which it belong to */ + for (i = 0; i < size; i++) { + k = (data[i] - min + 1) / 10;/* calculate the index of data[i] in barrel */ + (pBarrel + k)->node[(pBarrel + k)->count] = data[i]; + (pBarrel + k)->count++; + } + + pos = 0; + for (i = 0; i < num; i++) { + if ((pBarrel + i)->count != 0) + { + quick_sort((pBarrel+i)->node, 0, ((pBarrel+i)->count)-1);/* sort node in every barrel */ + + for (j = 0; j < (pBarrel+i)->count; j++) { + data[pos++] = (pBarrel+i)->node[j]; + } + } + } + free(pBarrel); +} + +void bucket_sort_test() +{ + int a[] = {78, 17, 39, 26, 72, 94, 21, 12, 23, 91}; + int size = sizeof(a) / sizeof(int); + printf("\r\n bucket sort test ..."); + bucket_sort(a, size); + dump(a,size); + +} + +int main() +{ + count_sort_test(); + + radix_sort_test(); + + bucket_sort_test(); + return 0; +} diff --git a/c-cpp/14_sorts/.gitkeep b/c-cpp/14_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/14_sorts/analytics_of_std_sort.md b/c-cpp/14_sorts/analytics_of_std_sort.md new file mode 100644 index 00000000..514e6f54 --- /dev/null +++ b/c-cpp/14_sorts/analytics_of_std_sort.md @@ -0,0 +1,8 @@ +# C++ STL 中的 std::sort 分析 + +参见 [Liam Huang 的博客](https://liam.page/)中的 3 篇文章: + +* [谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/) +* [谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/) +* [谈谈 STL 中的 std::sort](https://liam.page/2018/09/18/std-sort-in-STL/) + diff --git a/c-cpp/14_sorts/counting_sort.c b/c-cpp/14_sorts/counting_sort.c new file mode 100644 index 00000000..55e96088 --- /dev/null +++ b/c-cpp/14_sorts/counting_sort.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +void dump(int *arr, int size) +{ + int i; + + for (i = 0; i < size; i++) + printf("%08d\n", arr[i]); +} + +// content in arr must be positive integer +void counting_sort(int *arr, int size) +{ + int max, i; + int *count, *tmp; + + if (size <= 1) + return; + + max = 0; + // find the biggest integer + for (i = 0; i < size; i++) { + if (max < arr[i]) + max = arr[i]; + } + + // init count to 0 + count = (int*)malloc((max+1) * sizeof(int)); + tmp = (int*)malloc(size * sizeof(int)); + if (!count || !tmp) + return; + memset(count, 0, (max + 1) * sizeof(int)); + + // counting + for (i = 0; i < size; i++) + count[arr[i]]++; + for (i = 1; i < max + 1; i++) + count[i] = count[i-1] + count[i]; + + // iterate arr and put it to the correct index in tmp + for (i = 0; i < size; i++){ + int index = count[arr[i]] - 1; + tmp[index] = arr[i]; + count[arr[i]]--; + } + + // move back to arr + memcpy(arr, tmp, size * sizeof(int)); +} + +void counting_sort_test() +{ + int test_data[10] = {3, 23, 98, 1, 27, 36, 52, 89, 76, 44}; + + counting_sort(test_data, 10); + dump(test_data, 10); +} + +int main() +{ + counting_sort_test(); + return 0; +} diff --git a/c-cpp/15_bsearch/.gitkeep b/c-cpp/15_bsearch/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/15_bsearch/binary_search.c b/c-cpp/15_bsearch/binary_search.c new file mode 100644 index 00000000..1125fb55 --- /dev/null +++ b/c-cpp/15_bsearch/binary_search.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +typedef int(*bs)(int *arr, int size, int val); + +int binary_search_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (arr[mid] == val) + return mid; + + // mid == 0 means size == 1 + // so the only element in array doesn't equal to val + if (!mid) + return -1; + + if (arr[mid] < val) { + idx = binary_search_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } else { + idx = binary_search_r(arr, mid, val); + } + + return idx; +} + +int binary_search_i(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + if (arr[mid] == val) + return mid; + + if (arr[mid] < val) + low = mid + 1; + else + high = mid - 1; + } + + return -1; +} + +void iteratioin_test(bs binary_search) +{ + int arr[10] = {1, 4, 5, 9, 12, 19, 21, 28, 31, 36}; + int idx; + + idx = binary_search(arr, 10, 12); + if (idx != -1) + printf("find 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search(arr, 10, 13); + if (idx != -1) + printf("find 13 at %d\n", idx); + else + printf("13 not in arr \n"); + + idx = binary_search(arr, 10, 1); + if (idx != -1) + printf("find 1 at %d\n", idx); + else + printf("1 not in arr \n"); + + idx = binary_search(arr, 10, 36); + if (idx != -1) + printf("find 36 at %d\n", idx); + else + printf("36 not in arr \n"); + + idx = binary_search(arr, 10, 31); + if (idx != -1) + printf("find 31 at %d\n", idx); + else + printf("31 not in arr \n"); + +} + +int main() +{ + printf("=== Test iteration version:\n"); + iteratioin_test(binary_search_i); + + printf("=== Test recursive version:\n"); + iteratioin_test(binary_search_r); + + return 0; +} diff --git a/c-cpp/15_bsearch/bsearch.hpp b/c-cpp/15_bsearch/bsearch.hpp new file mode 100644 index 00000000..d55a0867 --- /dev/null +++ b/c-cpp/15_bsearch/bsearch.hpp @@ -0,0 +1,37 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/24. + */ + +#ifndef BSEARCH_BSEARCH_HPP_ +#define BSEARCH_BSEARCH_HPP_ + +#include +#include + +// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement, +// but with a bad time complexity. For better performance, iterators should meet +// the RandomAccessIterator requirement. +template ::value_type, + typename Compare = std::less> +IterT bsearch(IterT first, + IterT last, + ValueT target, + Compare comp = Compare()) { + IterT result = last; + while (std::distance(first, last) > 0) { + IterT mid = first + std::distance(first, last) / 2; + if (comp(*mid, target)) { + first = mid + 1; + } else if (comp(target, *mid)) { + last = mid; + } else { // equal + result = mid; + break; + } + } + return result; +} + +#endif // BSEARCH_BSEARCH_HPP_ + diff --git a/c-cpp/15_bsearch/bsearch_c/bsearch.c b/c-cpp/15_bsearch/bsearch_c/bsearch.c new file mode 100644 index 00000000..898c583f --- /dev/null +++ b/c-cpp/15_bsearch/bsearch_c/bsearch.c @@ -0,0 +1,88 @@ +/************************************************************************* + > File Name: bsearch.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-21 + > Desc: + ************************************************************************/ +#include +#include +#include + + +int mybsearch(int a[],int size,int value) +{ + int mid = 0; + int left = 0; + int right = size - 1; + + while(left <= right) + { + /*防止size数量太大是,(left + right)数据翻转,导致问题*/ + mid = left + ((right - left)>>1); + + if (a[mid] == value) + { + return mid; + } + else if (a[mid] < value) + { + left = mid + 1; + } + else + { + right = mid - 1; + } + } + + return -1; +} + +int helper(int a[], int left,int right,int value) +{ + int mid = 0; + + if (left > right) + { + return -1; + } + /*防止size数量太大是,(left + right)数据翻转,导致问题*/ + mid = left + ((right - left)>>1); + if (a[mid] == value) + { + return mid; + } + else if (a[mid] < value) + { + return helper(a,mid + 1,right,value); + } + else + { + return helper(a,left,mid - 1,value); + } + return -1; +} +/*递归实现*/ +int mybsearch_2(int a[],int size,int value) +{ + + return helper(a,0,size-1,value); +} + +int main() +{ + int a[10] = {5,6,8,9,10,11,23,42,53,123}; + int data = 0; + int res = 0; + + printf("\r\n输入一个整数"); + scanf("%d",&data); + res = mybsearch(a,10,data); + printf("data[%d] %s 在数据中,下标是%d",data,(res != -1)?"":"不",res); + + printf("\r\n输入一个整数"); + scanf("%d",&data); + res = mybsearch_2(a,10,data); + printf("data[%d] %s 在数据中,下标是%d",data,(res != -1)?"":"不",res); + return; +} diff --git a/c-cpp/15_bsearch/bsearch_c/sqrt.c b/c-cpp/15_bsearch/bsearch_c/sqrt.c new file mode 100644 index 00000000..7c7c3d48 --- /dev/null +++ b/c-cpp/15_bsearch/bsearch_c/sqrt.c @@ -0,0 +1,53 @@ +/************************************************************************* + > File Name: sqrt.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-31 + > Desc: + ************************************************************************/ +#include +#include +#include +#include + + +/*求解精度设置*/ +#define E 0.000001 +double mybsearch(double num) +{ + double start = 1.0; + double end = num; + double mid = 0.0; + while(1) + { + mid = (start + end)/2; + if(((mid*mid - num) <= E) && ((mid*mid - num) >= -E)) + { + return mid; + } + + if ((mid*mid - num) > E) + { + end = mid; + } + else + { + start = mid; + } + } + + return 0; +} + + +int main() +{ + double num = 0.0; + + /*这里需要注意:double的输入方式*/ + scanf("%lf",&num); + printf("\r\n num %lf的平方根是%lf",num,mybsearch(num)); + + return 0; +} + diff --git a/c-cpp/15_bsearch/bsearch_test.cc b/c-cpp/15_bsearch/bsearch_test.cc new file mode 100644 index 00000000..e5529915 --- /dev/null +++ b/c-cpp/15_bsearch/bsearch_test.cc @@ -0,0 +1,28 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/24. + */ + +#include +#include + +#include "bsearch.hpp" + +template +void test_bsearch(const VecT& test, T target) { + auto it = bsearch(test.begin(), test.end(), target); + std::cout << std::distance(test.begin(), it) << std::endl; +} + +int main() { + std::vector test{0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 7}; // std::less() + + test_bsearch(test, 8); // 14 + test_bsearch(test, -1); // 14 + test_bsearch(test, 0); // 0, 1 + test_bsearch(test, 4); // 5, 6 + test_bsearch(test, 5); // 7, 8, 9, 10, 11 + test_bsearch(test, 7); // 13 + + return 0; +} + diff --git a/c-cpp/16_bsearch/.gitkeep b/c-cpp/16_bsearch/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/16_bsearch/bsearch.c b/c-cpp/16_bsearch/bsearch.c new file mode 100644 index 00000000..17e06fd4 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch.c @@ -0,0 +1,190 @@ +/************************************************************************* + > File Name: bsearch.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-21 + > Desc: + ************************************************************************/ +#include +#include +#include + +/*二分查找算法的变形问题 + *1、查找第一个等于给定数值的元素 + *2、查找最后一个等于给定数值的元素 + *3、查找第一个大于等于给定数值的元素 + *4、查找第一个小于等于给定数值的元素 + * */ + + + /*1、查找第一个等于给定数值的元素*/ +int mybsearch_1(int a[],int size,int value) +{ + int mid = 0; + int left = 0; + int right = size - 1; + + while(left <= right) + { + /*防止size数量太大是,(left + right)数据翻转,导致问题*/ + mid = left + ((right - left)>>1); + + if (a[mid] < value) + { + left = mid + 1; + } + else if (a[mid] > value) + { + right = mid - 1; + } + else + { + if ((mid == 0) || (a[mid - 1] != value)) + { + return mid; + } + else + { + right = mid - 1; + } + } + } + + return -1; +} + + /*2、查找最后一个等于给定数值的元素*/ +int mybsearch_2(int a[],int size,int value) +{ + int mid = 0; + int left = 0; + int right = size - 1; + + while(left <= right) + { + /*防止size数量太大是,(left + right)数据翻转,导致问题*/ + mid = left + ((right - left)>>1); + + if (a[mid] < value) + { + left = mid + 1; + } + else if (a[mid] > value) + { + right = mid - 1; + } + else + { + if ((mid == (size - 1)) || (a[mid + 1] != value)) + { + return mid; + } + else + { + left = mid + 1; + } + } + } + + return -1; +} + /*3、查找第一个大于等于给定数值的元素*/ +int mybsearch_3(int a[],int size,int value) +{ + int mid = 0; + int left = 0; + int right = size - 1; + + while(left <= right) + { + /*防止size数量太大是,(left + right)数据翻转,导致问题*/ + mid = left + ((right - left)>>1); + + if (a[mid] < value) + { + left = mid + 1; + } + else + { + /*a[mid] >= value 当mid==0 或者a[mid-1] > value 说明是第一个大于等于value*/ + if ((mid == 0) || (a[mid - 1] < value)) + { + return mid; + } + else + { + right = mid - 1; + } + } + } + + return -1; +} + + /*4、查找第一个小于等于给定数值的元素*/ +int mybsearch_4(int a[],int size,int value) +{ + int mid = 0; + int left = 0; + int right = size - 1; + + while(left <= right) + { + /*防止size数量太大是,(left + right)数据翻转,导致问题*/ + mid = left + ((right - left)>>1); + + if (a[mid] > value) + { + right = mid - 1; + } + else + { + /*a[mid] <= value 时,当前mid == size -1 数组中最大的数值; + * 或者a[mid + 1] 大于vlaue,就是mid就第一个小于等于value*/ + if ((mid == (size - 1)) || (a[mid + 1] > value)) + { + return mid; + } + else + { + left = mid + 1; + } + } + } + + return -1; +} +int main() +{ + int a[10] = {5,6,6,9,10,11,11,22,33,33}; + int data = 0; + int i = 0; + int res =0; + + printf("\r\n"); + for(i = 0; i < 10 ; i++) + { + printf("%d ",a[i]); + } + printf("\r\n"); + printf("\r\n输入一个整数"); + scanf("%d",&data); + res = mybsearch_1(a,10,data); + printf("第一个等于data[%d],下标是%d",data,res); + + printf("\r\n输入一个整数"); + scanf("%d",&data); + res = mybsearch_2(a,10,data); + printf("最后一个等于data[%d],下标是%d",data,res); + + printf("\r\n输入一个整数"); + scanf("%d",&data); + res = mybsearch_2(a,10,data); + printf("第一个大于等于data[%d],下标是%d",data,res); + + printf("\r\n输入一个整数"); + scanf("%d",&data); + res = mybsearch_2(a,10,data); + printf("第一个小等于data[%d],下标是%d",data,res); + return; +} diff --git a/c-cpp/16_bsearch/bsearch_variant.c b/c-cpp/16_bsearch/bsearch_variant.c new file mode 100644 index 00000000..88271273 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch_variant.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include + + +int binary_search(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + if (arr[mid] == val) + return mid; + + if (arr[mid] < val) + low = mid + 1; + else + high = mid - 1; + } + + return -1; +} + +/* + * find the first index with *val* + * + * This is a little tricky because the calculation of mid is integer based, it + * will be cast to the lower bound of an integer. + * + * In case the [low, high] range is of size 1 or 2 and arr[mid] >= val, we will + * have: + * + * mid = (low + high) / 2 = low + * high = mid - 1 = low - 1 < low, which break the loop + * + */ +int binary_search_first(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + //printf("[%d-%d] %d\n", low, high, mid); + + if (arr[mid] >= val) + high = mid - 1; + else + low = mid + 1; + } + + //printf("[%d-%d] %d\n", low, high, mid); + if (arr[low] == val) + return low; + else + return -1; +} + +int binary_search_last(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (arr[mid] <= val) + low = mid + 1; + else + high = mid - 1; + } + + if (arr[high] == val) + return high; + else + return -1; +} + +int binary_search_first_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + // we find *val* at mid, try first half + if (arr[mid] == val) { + idx = binary_search_first_r(arr, mid, val); + return idx != -1 ? idx : mid; + } + + // mid == 0 means size == 1 + // so the only element in array doesn't equal to val + if (!mid) + return -1; + + if (arr[mid] < val) { + idx = binary_search_first_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } else { + idx = binary_search_first_r(arr, mid, val); + } + + return idx; +} + +int binary_search_last_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + // we find *val* at mid, try last half + if (arr[mid] == val) { + idx = binary_search_last_r(arr+mid+1, size-mid-1, val); + if (idx != -1) + mid += idx + 1; + return mid; + } + + // mid == 0 means size == 1 + // so the only element in array doesn't equal to val + if (!mid) + return -1; + + if (arr[mid] < val) { + idx = binary_search_last_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } else { + idx = binary_search_last_r(arr, mid, val); + } + + return idx; +} + +int binary_search_first_bigger(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (arr[mid] >= val) { + if (mid == 0 || arr[mid-1] < val) + return mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + + return -1; +} + +int binary_search_first_bigger_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + if (arr[mid] >= val) { + // find one bigger than val, try first half + idx = binary_search_first_bigger_r(arr, mid, val); + if (idx == -1) + idx = mid; + } else { + // the bigger one may sit in second half + idx = binary_search_first_bigger_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + } + + return idx; +} + +int binary_search_last_smaller(int *arr, int size, int val) +{ + int low = 0, high = size - 1, mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (arr[mid] <= val) { + if (mid == 0 || arr[mid+1] > val) + return mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + + return -1; +} + +int binary_search_last_smaller_r(int *arr, int size, int val) +{ + int mid = size / 2; + int idx; + + if (size <= 0) + return -1; + + if (arr[mid] <= val) { + // find one smaller than val, try second half + idx = binary_search_last_smaller_r(arr + mid + 1, size - mid - 1, val); + if (idx != -1) + idx += mid + 1; + else + idx = mid; + } else { + // the smaller one may sit in first half + idx = binary_search_last_smaller_r(arr, mid, val); + } + + return idx; +} + +int main() +{ + int arr[10] = {1, 4, 5, 9, 12, 14, 19, 19, 31, 36}; + int idx; + + printf("Test Array:\n"); + for (idx = 0; idx < 10; idx++) + printf("%8d", arr[idx]); + printf("\n"); + + idx = binary_search_first(arr, 10, 19); + if (idx != -1) + printf("first 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_first_r(arr, 10, 19); + if (idx != -1) + printf("first 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_last(arr, 10, 19); + if (idx != -1) + printf("last 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_last_r(arr, 10, 19); + if (idx != -1) + printf("last 19 at %d\n", idx); + else + printf("19 not in arr \n"); + + idx = binary_search_first_bigger(arr, 10, 12); + if (idx != -1) + printf("first bigger 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search_first_bigger_r(arr, 10, 12); + if (idx != -1) + printf("first bigger 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search_last_smaller(arr, 10, 12); + if (idx != -1) + printf("last smaller 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + idx = binary_search_last_smaller_r(arr, 10, 12); + if (idx != -1) + printf("last smaller 12 at %d\n", idx); + else + printf("12 not in arr \n"); + + return 0; +} diff --git a/c-cpp/16_bsearch/bsearch_varients.hpp b/c-cpp/16_bsearch/bsearch_varients.hpp new file mode 100644 index 00000000..4ea53363 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch_varients.hpp @@ -0,0 +1,90 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef BSEARCH_BSEARCH_VARIENTS_HPP_ +#define BSEARCH_BSEARCH_VARIENTS_HPP_ + +#include +#include + +enum class BsearchPolicy { UNSPECIFIED, FIRST, LAST, FIRST_NOT_LESS, LAST_NOT_GREATER }; + +// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement, +// but with a bad time complexity. For better performance, iterators should meet +// the RandomAccessIterator requirement. +template ::value_type, + typename Compare> +IterT bsearch(IterT first, + IterT last, + ValueT target, + Compare comp, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + IterT result = last; + while (std::distance(first, last) > 0) { + IterT mid = first + std::distance(first, last) / 2; + if (policy == BsearchPolicy::FIRST_NOT_LESS) { + if (!comp(*mid, target)) { + if (mid == first or comp(*(mid - 1), target)) { + result = mid; + break; + } else { + last = mid; + } + } else { + first = mid + 1; + } + } else if (policy == BsearchPolicy::LAST_NOT_GREATER) { + if (comp(target, *mid)) { + last = mid; + } else { + if (std::distance(mid, last) == 1 or comp(target, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } + } else { // policy == UNSPECIFIED or FIRST or LAST + if (comp(*mid, target)) { + first = mid + 1; + } else if (comp(target, *mid)) { + last = mid; + } else { // equal + if (policy == BsearchPolicy::FIRST) { + if (mid == first or comp(*(mid - 1), *mid)) { + result = mid; + break; + } else { + last = mid; + } + } else if (policy == BsearchPolicy::LAST) { + if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } else { + result = mid; + break; + } + } + } + } + return result; +} + +template ::value_type, + typename Compare = std::less> +IterT bsearch(IterT first, + IterT last, + ValueT target, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + return bsearch(first, last, target, Compare(), policy); +} + +#endif // BSEARCH_BSEARCH_VARIENTS_HPP_ + diff --git a/c-cpp/16_bsearch/bsearch_varients_test.cc b/c-cpp/16_bsearch/bsearch_varients_test.cc new file mode 100644 index 00000000..34a22529 --- /dev/null +++ b/c-cpp/16_bsearch/bsearch_varients_test.cc @@ -0,0 +1,41 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#include +#include + +#include "bsearch_varients.hpp" + +template +void test_bsearch(const VecT& test, + T target, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + auto it = bsearch(test.begin(), test.end(), target, policy); + std::cout << std::distance(test.begin(), it) << std::endl; +} + +int main() { + std::vector test{0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 8}; // std::less() + + test_bsearch(test, 8); // 14 + test_bsearch(test, -1); // 14 + test_bsearch(test, 0); // 0, 1 + test_bsearch(test, 0, BsearchPolicy::FIRST); // 0 + test_bsearch(test, 0, BsearchPolicy::LAST); // 1 + test_bsearch(test, 4); // 5, 6 + test_bsearch(test, 4, BsearchPolicy::FIRST); // 5 + test_bsearch(test, 4, BsearchPolicy::LAST); // 6 + test_bsearch(test, 5); // 7, 8, 9, 10, 11 + test_bsearch(test, 5, BsearchPolicy::FIRST); // 7 + test_bsearch(test, 5, BsearchPolicy::LAST); // 11 + test_bsearch(test, 7, BsearchPolicy::FIRST_NOT_LESS); // 13 + test_bsearch(test, 7, BsearchPolicy::LAST_NOT_GREATER); // 12 + test_bsearch(test, 7, BsearchPolicy::FIRST); // 14 + test_bsearch(test, 8); // 13 + test_bsearch(test, 8, BsearchPolicy::FIRST); // 13 + test_bsearch(test, 8, BsearchPolicy::LAST); // 13 + + return 0; +} + diff --git a/c-cpp/17_skiplist/.gitkeep b/c-cpp/17_skiplist/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/17_skiplist/SkipList.cpp b/c-cpp/17_skiplist/SkipList.cpp new file mode 100644 index 00000000..ffef260b --- /dev/null +++ b/c-cpp/17_skiplist/SkipList.cpp @@ -0,0 +1,363 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +/** + * һʵַ + * д洢Ҵ洢Dzظġ + * + * C++汾. + * JAVA汾 ԭ AuthorZHENG + * + * Authorpuhuaqiang + * + * ṹ: + * + * K 1 9 + * K-1 1 5 9 + * K-2 1 3 5 7 9 + * ... .... + * 0(ԭʼ) 1 2 3 4 5 6 7 8 9 + */ + +const int MAX_LEVEL = 16; + +/** + * @brief ڵ +*/ +class CNode +{ +public: + CNode(); + ~CNode(); + + std::string toString(); + /** + * @brief ȡ + */ + CNode** GetIdxList(); + + /** + * @brief + */ + void SetData(int v); + /** + * @brief ȡ + */ + int GetData(); + /** + * @brief + */ + void SetLevel(int l); +private: + /**ǰڵֵ*/ + int m_data; + /** + * ǰڵÿȼһڵ. + * 2 N1 N2 + * 1 N1 N2 + * N1DZڵ, m_lpForwards[x] N2 + * + * [0] ԭʼ. + */ + CNode* m_lpForwards[MAX_LEVEL]; + /**ǰڵڵ*/ + int m_iMaxLevel; +}; + +/** + * @brief +*/ +class CSkipList +{ +public: + CSkipList(); + ~CSkipList(); + /** + * @brief ֵָĽڵ + * @param v + */ + CNode* Find(int v); + /** + * @brief ֵָ + * @param v + */ + void Insert(int v); + /** + * @brief ɾֵָĽڵ + * @param v + */ + int Delete(int v); + void PrintAll(); + /** + * @brief ӡṹ + * @param l -1ʱӡмĽṹ >=0ʱӡָĽṹ + */ + void PrintAll(int l); + /** + * @brief ڵʱ,õK + * @return K + */ + int RandomLevel(); + +private: + int levelCount; + /** + * + * ͷ/(ڵ) + */ + CNode* m_lpHead; +}; + +int main() +{ + CSkipList skipList; + /// ԭʼֵ + for(int i=1; i< 50; i++){ + if((i%3) == 0){ + skipList.Insert(i); + } + } + for(int i=1; i< 50; i++){ + if((i%3) == 1){ + skipList.Insert(i); + } + } + skipList.PrintAll(); + std::cout<GetData()< k-1 -> k-2 ...->0 + */ + for(int i=levelCount-1; i>=0; --i){ + /** + * СvĽڵ(lpNode). + */ + while((NULL != lpNode->GetIdxList()[i]) && (lpNode->GetIdxList()[i]->GetData() < v)){ + lpNode = lpNode->GetIdxList()[i]; + } + } + /** + * lpNode СvĽڵ, lpNodeһڵ͵ڻvĽڵ + */ + if((NULL != lpNode->GetIdxList()[0]) && (lpNode->GetIdxList()[0]->GetData() == v)){ + return lpNode->GetIdxList()[0]; + } + return NULL; +} +void CSkipList::Insert(int v) +{ + /// ½ڵ + CNode* lpNewNode = new CNode(); + if(NULL == lpNewNode){ + return; + } + + /** + * ½ڵֲڵ + * 3, µĽڵ123ϵ + */ + int level = RandomLevel(); + lpNewNode->SetData(v); + lpNewNode->SetLevel(level); + + /** + * ʱ + * ҪǵõµĽڵÿϵλ + */ + CNode *lpUpdateNode[level]; + for(int i=0; i= 0; --i){ + /** + * λ + * eg. 1 1 7 10 + * 6 + * lpFind->GetIdxList()[i]->GetData() : ʾڵlpFindڵ1һڵ + * "lpFind->GetIdxList()[i]->GetData() < v"ʱ, + * ½ڵҪ뵽 lpFindڵĺ, lpFind->GetIdxList()[i] ڵǰ + * lpFind1 lpFind->GetIdxList()[i] 7 + */ + while((NULL != lpFind->GetIdxList()[i]) && (lpFind->GetIdxList()[i]->GetData() < v)){ + lpFind = lpFind->GetIdxList()[i]; + } + /// lpFind ½ڵ iĺһڵ + lpUpdateNode[i] = lpFind; + } + + for(int i=0; iGetIdxList()[i]ڵ7 + * + * 2 6 17֮ + */ + lpNewNode->GetIdxList()[i] = lpUpdateNode[i]->GetIdxList()[i]; + lpUpdateNode[i]->GetIdxList()[i] = lpNewNode; + } + if(levelCount < level){ + levelCount = level; + } +} +int CSkipList::Delete(int v) +{ + int ret = -1; + CNode *lpUpdateNode[levelCount]; + CNode *lpFind = m_lpHead; + for(int i=levelCount-1; i>= 0; --i){ + /** + * СvĽڵ(lpFind). + */ + while((NULL != lpFind->GetIdxList()[i]) && (lpFind->GetIdxList()[i]->GetData() < v)){ + lpFind = lpFind->GetIdxList()[i]; + } + lpUpdateNode[i] = lpFind; + } + /** + * lpFind СvĽڵ, lpFindһڵ͵ڻvĽڵ + */ + if((NULL != lpFind->GetIdxList()[0]) && (lpFind->GetIdxList()[0]->GetData() == v)){ + for(int i=levelCount-1; i>=0; --i){ + if((NULL != lpUpdateNode[i]->GetIdxList()[i]) && (v == lpUpdateNode[i]->GetIdxList()[i]->GetData())){ + lpUpdateNode[i]->GetIdxList()[i] = lpUpdateNode[i]->GetIdxList()[i]->GetIdxList()[i]; + ret = 0; + } + } + } + return ret; +} +void CSkipList::PrintAll() +{ + CNode* lpNode = m_lpHead; + while(NULL != lpNode->GetIdxList()[0]){ + std::cout<GetIdxList()[0]->toString().data()<GetIdxList()[0]; + } +} +void CSkipList::PrintAll(int l) +{ + for(int i=MAX_LEVEL-1; i>=0;--i){ + CNode* lpNode = m_lpHead; + std::cout<<""<= 0) && (l == i))){ + while(NULL != lpNode->GetIdxList()[i]){ + std::cout<GetIdxList()[i]->GetData()<<" "; + lpNode = lpNode->GetIdxList()[i]; + } + std::cout<= 0){ + break; + } + } + } +} +int GetRandom() +{ + static int _count = 1; + std::default_random_engine generator(time(0) + _count); + std::uniform_int_distribution distribution(1,99999/*0x7FFFFFFF*/); + int dice_roll = distribution(generator); + _count += 100; + return dice_roll; +} +int CSkipList::RandomLevel() +{ + int level = 1; + for(int i=1; i +#include +#include +#include +#include + +// https://www.youtube.com/watch?v=2g9OSRKJuzM&t=17s + +#define MAX_LEVEL 15 + +struct node { + int val; + int max_level; + struct node *forward[MAX_LEVEL]; +}; + +struct skip_list { + struct node head; + int max_level; + int max_level_nodes; +}; + +void node_init(struct node* node) +{ + memset(node, 0, sizeof(struct node)); +} + +void skip_list_init(struct skip_list* sl) +{ + node_init(&sl->head); + sl->max_level = 0; + sl->max_level_nodes = 0; +} + +void random_init() +{ + static bool done = false; + + if (done) + return; + + srandom(time(NULL)); + done = true; +} + +int random_level(void) +{ + int i, level = 1; + + random_init(); + + for (i = 1; i < MAX_LEVEL; i++) + if (random() % 2 == 1) + level++; + + return level; +} + +void random_level_test() +{ + printf("random level %d\n", random_level()); + printf("random level %d\n", random_level()); + printf("random level %d\n", random_level()); + printf("random level %d\n", random_level()); + printf("random level %d\n", random_level()); +} + +void insert(struct skip_list *sl, int val) +{ + int level = random_level(); + struct node *update[MAX_LEVEL]; + struct node *new, *p; + int i; + + new = (struct node*)malloc(sizeof(struct node)); + if (!new) + return; + + new->max_level = level; + new->val = val; + + for (int i = 0; i < MAX_LEVEL; i++) + update[i] = &sl->head; + + p = &sl->head; + for (i = level - 1; i >= 0; i--) { + while(p->forward[i] && p->forward[i]->val < val) + p = p->forward[i]; + + update[i] = p; + } + + for (i = 0; i < level; i++) { + new->forward[i] = update[i]->forward[i]; + update[i]->forward[i] = new; + } + + if (sl->max_level < level) { + sl->max_level = level; + sl->max_level_nodes = 1; + } else if (sl->max_level == level) + sl->max_level_nodes++; +} + +struct node *find(struct skip_list* sl, int val) +{ + struct node *node = &sl->head; + int i; + + for (i = sl->max_level - 1; i >= 0; i--) { + while (node->forward[i] && node->forward[i]->val < val) + node = node->forward[i]; + } + + if (node->forward[0] && node->forward[0]->val == val) { + return node->forward[0]; + } + else + return NULL; +} + +void delete(struct skip_list* sl, int val) +{ + struct node *update[MAX_LEVEL]; + struct node *p; + int i; + + p = &sl->head; + + for (i = sl->max_level; i >= 0; i--) { + while (p->forward[i] && p->forward[i]->val < val) + p = p->forward[i]; + + update[i] = p; + } + + if (p->forward[0] == NULL || p->forward[0]->val != val) + return; + + if (p->forward[0]->max_level == sl->max_level) + sl->max_level_nodes--; + + for (i = sl->max_level-1; i >= 0; i--) { + if (update[i]->forward[i] && update[i]->forward[i]->val == val) + update[i]->forward[i] = update[i]->forward[i]->forward[i]; + } + + // fixup max_level and max_level_nodes + if (sl->max_level_nodes == 0) { + //sl->max_level--; + p = &sl->head; + // skip (max_level - 1), direct test (max_level - 2) + // since no nodes on (max_level - 1) + for (i = sl->max_level - 2; i >= 0; i--) { + while (p->forward[i]) { + sl->max_level_nodes++; + p = p->forward[i]; + } + + if (sl->max_level_nodes) { + sl->max_level = i + 1; + break; + } else + sl->max_level = i; + } + } +} + + +void print_sl(struct skip_list* sl) +{ + struct node *node; + int level; + + printf("%d level skip list with %d nodes on top\n", + sl->max_level, sl->max_level_nodes); + + for (level = sl->max_level - 1; level >= 0; level--) { + node = &sl->head; + printf("Level[%02d]:", level); + while (node->forward[level]) { + printf("%4d", node->forward[level]->val); + node = node->forward[level]; + } + printf("\n"); + } +} + +int main() +{ + struct skip_list sl; + struct node *node = NULL; + int i; + + skip_list_init(&sl); + print_sl(&sl); + + for (i = 0; i < 10; i++) + insert(&sl, i); + print_sl(&sl); + + node = find(&sl, 8); + if (node) + printf("find 8 in sl %d\n", node->val); + else + printf("8 not in sl\n"); + + for (i = 0; i < 10; i++) { + delete(&sl, i); + print_sl(&sl); + } + + return 0; +} diff --git a/c-cpp/17_skiplist/skiplist.hpp b/c-cpp/17_skiplist/skiplist.hpp new file mode 100644 index 00000000..9cbfb45a --- /dev/null +++ b/c-cpp/17_skiplist/skiplist.hpp @@ -0,0 +1,186 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/29. + */ + +#ifndef SKIPLIST_SKIPLIST_HPP_ +#define SKIPLIST_SKIPLIST_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +template +class skiplist { + public: + using value_type = Value; + using hash_type = std::hash; + using key_type = typename hash_type::result_type; + using size_type = size_t; + + private: + struct InternalNode { + value_type value; + const key_type key; + std::vector forwards; // pointers to successor nodes + + InternalNode(const key_type& k, const size_type lv) + : value(), key(k), forwards(lv, nullptr) {} + InternalNode(const value_type& v, const size_type lv) + : value(v), key(hash_type()(value)), forwards(lv, nullptr) {} + }; + using node_type = InternalNode; + + private: + const size_type MAX_LEVEL = 16; + const double PROBABILITY = 0.5; + const unsigned int seed = + std::chrono::system_clock::now().time_since_epoch().count(); + mutable + std::default_random_engine generator = std::default_random_engine(seed); + mutable + std::binomial_distribution distribution = + std::binomial_distribution(MAX_LEVEL - 1, PROBABILITY); + node_type* head = nullptr; + node_type* nil = nullptr; + static const value_type default_value; + + public: + skiplist() { + key_type head_key = std::numeric_limits::min(); + key_type nil_key = std::numeric_limits::max(); + head = new node_type(head_key, MAX_LEVEL); + nil = new node_type(nil_key, MAX_LEVEL); + std::fill(head->forwards.begin(), head->forwards.end(), nil); + } + skiplist(std::initializer_list init) : skiplist() { + for (const value_type& v : init) { + insert(v); + } + } + skiplist(const skiplist& other) = delete; + skiplist(skiplist&& other) : + MAX_LEVEL(std::move(other.MAX_LEVEL)), + PROBABILITY(std::move(other.PROBABILITY)), + seed(std::move(other.seed)), + generator(std::move(other.generator)), + distribution(std::move(other.distribution)), + head(other.head), + nil(other.nil) { + other.head = nullptr; + other.nil = nullptr; + } + ~skiplist() { + node_type* node = head; + while (nullptr != node and nullptr != node->forwards[0]) { + node_type* tmp = node; + node = node->forwards[0]; + delete tmp; + } + delete node; + } + skiplist& operator=(const skiplist& other) = delete; + skiplist& operator=(skiplist&& other) = delete; + + private: + inline size_type get_random_level() const { + return distribution(generator); + } + static size_type get_node_level(const node_type* node) { + return node->forwards.size(); + } + static node_type* make_node(const value_type& v, const size_type lv) { + return new node_type(v, lv); + } + /** + * @brief returns a pointer to the first node such that + * node->key == hash_type()(v) and node->value == v. + */ + node_type* get_first_equal(const value_type& v) const { + const key_type target = hash_type()(v); + node_type* x = head; + for (size_type i = get_node_level(head); i > 0; --i) { + while (x->forwards[i - 1]->key < target or + x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) { + x = x->forwards[i - 1]; + } + } + return x->forwards[0]; + } + /** + * @brief returns a collection of nodes. + * returns[i] is the pointer to the last node at level i + 1 + * such that returns[i]->key < hash_type()(v) or + * returns[i]->key == hash_type()(v) but returns[i]->value != v. + */ + std::vector get_predecessors(const value_type& v) const { + const key_type target = hash_type()(v); + std::vector results(get_node_level(head), nullptr); + node_type* x = head; + for (size_type i = get_node_level(head); i > 0; --i) { + while (x->forwards[i - 1]->key < target or + x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) { + x = x->forwards[i - 1]; + } + results[i - 1] = x; + } + return results; + } + + public: + const value_type& find(const value_type& target) { + node_type* x = get_first_equal(target); + if (nullptr != x and nil != x and x->value == target) { + return x->value; + } else { + return default_value; + } + } + void insert(const value_type& value) { + std::vector preds = get_predecessors(value); + const size_type new_node_lv = get_random_level(); + node_type* new_node = make_node(value, new_node_lv); + for (size_type i = 0; i != new_node_lv; ++i) { + new_node->forwards[i] = preds[i]->forwards[i]; + preds[i]->forwards[i] = new_node; + } + } + void erase(const value_type& value) { + std::vector preds = get_predecessors(value); + + node_type* node = preds[0]->forwards[0]; + if (node == nil or node->value != value) { return; } + + for (size_type i = 0; i != get_node_level(node); ++i) { + preds[i]->forwards[i] = node->forwards[i]; + } + delete node; + } + void print(std::ostream& os) const { + node_type* list = head->forwards[0]; + os << "{"; + + while (list != nil) { + os << "key: " << list->key << " value: " << list->value + << " level: " << get_node_level(list); + + list = list->forwards[0]; + + if (list != nil) os << " : "; + + os << "\n"; + } + os << "}\n"; + } +}; + +template +const typename skiplist::value_type skiplist::default_value = + typename skiplist::value_type(); + +#endif // SKIPLIST_SKIPLIST_HPP_ + diff --git a/c-cpp/17_skiplist/skiplist_c/skiplist.c b/c-cpp/17_skiplist/skiplist_c/skiplist.c new file mode 100644 index 00000000..843d575e --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_c/skiplist.c @@ -0,0 +1,346 @@ +/************************************************************************* + > File Name: skiplist.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-31 + > Desc: + ************************************************************************/ +#include +#include +#include +#include +#include"./skiplist.h" + + +/*创建node节点*/ +node* skip_list_create_node(int level,int key,int value) +{ + node * tmp = NULL; + + tmp =(node *)malloc(sizeof(node) + level*sizeof(node *)); + assert(tmp != NULL); + + memset(tmp,0,sizeof(node) + level*sizeof(node*)); + tmp->key = key; + tmp->value = value; + tmp->max_level = level; + + return tmp; +} + +/*创建跳表的表头,max_level层数*/ +skiplist * skip_list_create(int max_level) +{ + int i = 0; + skiplist * list = NULL; + + list = (skiplist *)malloc (sizeof(skiplist)); + assert(list != NULL); + + list->level = 1; + list->count = 0; + + list->head = skip_list_create_node(max_level,0,0); + if(list->head == NULL) + { + + free(list); + return NULL; + } + + return list; +} + +/*skiplist 销毁*/ +void skip_list_destory(skiplist * list) +{ + int i = 0; + node * tmp = NULL; + + if((list == NULL) || (list->head == NULL)) + { + return; + } + while(list->head->next[0] != NULL) + { + tmp = list->head->next[0]; + list->head->next[0] = tmp->next[0]; + free(tmp); + } + + free(list->head); + free(list); + return; +} + +/*插入元素获得层数,是随机产生的*/ +int skip_list_level(skiplist * list) +{ + int i = 0; + int level = 1; + for (i = 1; i < list->head->max_level; i++) + { + if ((rand()%2) == 1) + { + level++; + } + } + + return level; +} +int skip_list_insert(skiplist *list,int key,int value) +{ + int i = 0; + int level = 0; + node **update = NULL;/*用来更新每层的指针*/ + node *tmp = NULL; + node *prev = NULL; + + if (list == NULL) + { + return 1; + } + + /*申请update空间用于保存每层的指针*/ + update = (node **)malloc(sizeof(node *)*list->head->max_level); + if (update == NULL) + { + return 2; + } + + /*逐层查询节点的*/ + prev = list->head; + for (i = (list->level -1); i >= 0; i--) + { + /*初始化每level层的头指针*/ + while(((tmp = prev->next[i]) != NULL) && (tmp->key < key)) + { + prev = tmp; + } + update[i] = prev; + } + + /*当前key已经存在,返回错误*/ + if ((tmp!= NULL) && (tmp->key == key)) + { + return 3; + } + /*获取插入元素的随机层数,并更新跳表的最大层数*/ + level = skip_list_level(list); + /*创建当前数据节点*/ + tmp = skip_list_create_node(level,key,value); + if (tmp == NULL) + { + return 4; + } + + /*更新最大层数*/ + if (level > list->level) + { + for (i = list->level;i < level; i ++) + { + update[i] = list->head; + } + list->level = level; + } + + /*逐层更新节点的指针*/ + for(i = 0; i < level; i++) + { + tmp->next[i] = update[i]->next[i]; + update[i]->next[i] = tmp; + } + + list->count++; + return 0; +} + +int skip_list_delete(skiplist * list, int key ,int *value) +{ + int i = 0; + node **update = NULL;/*用来更新每层的指针*/ + node *tmp = NULL; + node *prev = NULL; + + if ((list == NULL) && (value == NULL)&& (list->count == 0)) + { + return 1; + } + /*申请update空间用于保存每层的指针*/ + update = (node **)malloc(sizeof(node *)*list->level); + if (update == NULL) + { + return 2; + } + /*逐层查询节点的*/ + prev = list->head; + for (i = (list->level -1); i >= 0; i--) + { + /*初始化每level层的头指针*/ + while(((tmp = prev->next[i]) != NULL) && (tmp->key < key)) + { + prev = tmp; + } + update[i] = prev; + } + + if ((tmp != NULL) + && (tmp->key == key)) + { + *value = tmp->value; + /*逐层删除*/ + for(i = 0; i < list->level; i++) + { + if(update[i]->next[i] == tmp) + { + update[i]->next[i] = tmp->next[i]; + } + } + + free(tmp); + tmp = NULL; + + /*更新level的层数*/ + for (i = list->level - 1; i >= 0; i++) + { + if (list->head->next[i] == NULL ) + { + list->level--; + } + else + { + break; + } + } + + list->count--; + + } + else + { + return 3;/*未找到节点*/ + } + + return 0 ; +} + +/*查询当前key是否在跳表中,如果存在返回查询的value数值,不存在返回-1*/ +int skip_list_search(skiplist *list,int key,int *value) +{ + int i = 0; + node *prev = NULL; + node *tmp = NULL; + + if((list == NULL) || (list->count == 0) || (value == NULL)) + { + return 1; + } + prev = list->head; + for(i = list->level - 1; i >= 0; i--) + { + while(((tmp = prev->next[i]) != NULL) && (tmp->key <= key)) + { + if (tmp->key == key) + { + *value = tmp->value; + return 0; + } + prev = tmp; + } + } + + return -1; +} + +void skip_list_dump(skiplist *list) +{ + int i = 0; + node *ptmp = NULL; + printf("\r\n----------------------------------------------"); + printf("\r\n skip list level[%d],count[%d]",list->level,list->count); + for(i = list->level - 1; i >= 0; i --) + { + ptmp = list->head->next[i]; + printf("\r\n level[%d]:",i); + while(ptmp != NULL) + { + printf("%d-%d ",ptmp->key,ptmp->value); + ptmp = ptmp->next[i]; + } + } + printf("\r\n----------------------------------------------"); + return; +} + +int main() +{ + int res = 0; + int key = 0; + int value = 0; + skiplist *list = NULL; + + + list = skip_list_create(5); + assert(list != NULL); + + while(1) + { + printf("\r\n 请输入key 和 value,当key = 1000时,退出输入:"); + scanf("%d%d",&key,&value); + if (key == 1000) + { + break; + } + res = skip_list_insert(list,key,value); + if (res != 0) + { + printf("\r\n skip list insert %d,failed,res=%d.",key,res); + } + } + skip_list_dump(list); + + while(1) + { + printf("\r\n 通过key 查询value的数值,当key = 1000时,退出查询"); + scanf("%d",&key); + if(key == 1000) + { + break; + } + res = skip_list_search(list,key,&value); + if (res != 0) + { + printf("\r\n skip list search %d,failed,res=%d.",key,res); + } + else + { + printf("\r\n skip list search %d,sucessful,value=%d.",key,value); + + } + } + skip_list_dump(list); + while(1) + { + printf("\r\n 通过key 删除节点,当key = 1000时,退出删除"); + scanf("%d",&key); + if(key == 1000) + { + break; + } + res = skip_list_delete(list,key,&value); + if (res != 0) + { + printf("\r\n skip list search %d,failed,res=%d.",key,res); + } + else + { + printf("\r\n skip list search %d,sucessful,value=%d.",key,value); + + } + } + + skip_list_dump(list); + skip_list_destory(list); + + return 0; +} diff --git a/c-cpp/17_skiplist/skiplist_c/skiplist.h b/c-cpp/17_skiplist/skiplist_c/skiplist.h new file mode 100644 index 00000000..217c9df0 --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_c/skiplist.h @@ -0,0 +1,33 @@ +/************************************************************************* + > File Name: skiplist.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-31 + > Desc: + ************************************************************************/ +#ifndef __SKIP_LIST_H__ +#define __SKIP_LIST_H__ + + +typedef struct _node +{ + int key; /*key是唯一的*/ + int value; /*存储的内容*/ + int max_level; /*当前节点最大层数*/ + struct _node *next[0];/*level层链表结构*/ +}node; + +typedef struct _skiplist +{ + int level; + int count; + node *head; +}skiplist; + +/*根据当前结构体元素的地址,获取到结构体首地址*/ +#define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define container(ptr,type,member) ({\ + const typeof( ((type *)0)->member) *__mptr = (ptr);\ + (type *) ( (char *)__mptr - offsetof(type,member));}) + +#endif diff --git a/c-cpp/17_skiplist/skiplist_test.cc b/c-cpp/17_skiplist/skiplist_test.cc new file mode 100644 index 00000000..ced124e8 --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_test.cc @@ -0,0 +1,67 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/30. + */ + +#include +#include + +#include "skiplist.hpp" + +int main() { + // 1. Initialize a skip list for test + // * default constructor + // * constructor with initializer list + // * insert + skiplist ss{"1", "2", "3", "4", "5"}; + + // 1a. show + // * print + ss.print(std::cout); + std::cout << std::endl; + + // 2. move construction + // * move constructor + skiplist s(std::move(ss)); + + // 2a. show + // * print + s.print(std::cout); + std::cout << std::endl; + + // 3.a find something doesn't exist. + // * find + auto f = s.find("0"); + if (!f.empty()) { + std::cout << "Node found!\nvalue: " << f << '\n'; + } else { + std::cout << "Node NOT found!\n"; + } + + // 3.b find something does exist. + // * find + auto ff = s.find("1"); + if (!ff.empty()) { + std::cout << "Node found!\tvalue: " << ff << '\n'; + } else { + std::cout << "Node NOT found!\n"; + } + + // 4. insert() - reassign + s.insert("TEST"); + + // 4a. print() + s.print(std::cout); + std::cout << std::endl; + + // 5. erase() + s.erase("TEST"); + + // 5a. print(); + s.print(std::cout); + std::cout << std::endl; + + std::cout << "\nDone!\n"; + + return 0; + // 6. destructor +} diff --git a/c-cpp/17_skiplist/skiplist_tr.hpp b/c-cpp/17_skiplist/skiplist_tr.hpp new file mode 100644 index 00000000..8e931d7f --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_tr.hpp @@ -0,0 +1,362 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/30. + */ + +#ifndef SKIPLIST_SKIPLIST_TR_HPP_ +#define SKIPLIST_SKIPLIST_TR_HPP_ + +#ifdef LIAM_UT_DEBUG_ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace skiplist_detail { +template +struct InternalNode { + using iterator = typename std::list::iterator; + const Key key; + std::multiset values; + std::vector forwards; + + InternalNode() = delete; + explicit InternalNode(const Key& k) : key(k) {} +}; + +template +class random_level { + private: + mutable std::random_device rd; + mutable std::mt19937 gen = std::mt19937(rd()); + mutable std::binomial_distribution dist; + + public: + random_level(IntType max_level, double prob) : dist(max_level - 1, prob) {} + inline IntType operator()() const { return dist(gen); } +}; +} // namespace skiplist_detail + +enum class erase_policy { ALL, SINGLE }; + +template , + size_t Factor = 2> +class skiplist { + public: + using value_type = Value; + using size_type = size_t; + using hasher = Hash; + using hash_type = typename Hash::result_type; + using compare = std::less; + using node_type = skiplist_detail::InternalNode; + using container = std::list; + using iterator = typename container::iterator; + using const_iterator = typename container::const_iterator; + static_assert(std::is_same::value, + "STATIC ASSERT FAILED! iterator type differs."); + + private: + size_type max_lv_ = 2; + double prob_ = 0.5; + mutable skiplist_detail::random_level rl_; + container cont_; + + public: + skiplist() : rl_(max_lv_, prob_) { + init_internally(); + } + explicit skiplist(const size_type max_lv, const double prob = 0.5) + : max_lv_(max_lv), prob_(prob), rl_(max_lv_, prob_) { + init_internally(); + } + skiplist(skiplist&& other) = default; + skiplist& operator=(skiplist&& other) = default; + ~skiplist() = default; + template + skiplist(InputIt first, InputIt last) : skiplist() { + using value_type_in_iter = typename std::iterator_traits::value_type; + static_assert(std::is_same::value, + "STATIC ASSERT FAILED! Value in InputIt should be the same to value_type."); + for (InputIt i = first; i != last; ++i) { + insert(*i); + } + } + skiplist(std::initializer_list init) : skiplist(init.begin(), init.end()) {} + + private: // noncopyable + skiplist(const skiplist&) = delete; + skiplist& operator=(const skiplist&) = delete; + + private: + void init_internally() { + const hash_type tail_key = std::numeric_limits::max(); + node_type tail(tail_key); + tail.forwards.resize(max_lv_, cont_.end()); + iterator tail_iter = cont_.insert(cont_.begin(), std::move(tail)); + + const hash_type head_key = std::numeric_limits::min(); + node_type head(head_key); + head.forwards.resize(max_lv_, tail_iter); + cont_.insert(cont_.begin(), std::move(head)); + +#ifdef LIAM_UT_DEBUG_ + assert(cont_.begin()->key == head_key); + for (auto it : cont_.begin()->forwards) { + assert(it->key == tail_key); + } + for (auto it : std::next(cont_.begin())->forwards) { + assert(it == cont_.end()); + } + std::cerr << "UT_DEBUG: all assert in init_internally() success!\n"; +#endif + + return; + } + /** + * @brief return a const_iterator points to the last element + * such that its hash_key <= target_hash_key + */ + const_iterator find_helper(const hash_type& key) const { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "Keys contained in the list: "; + for (auto node : cont_) { + std::cerr << node.key << ' '; + } + std::cerr << '\n'; + std::cerr << "Target key: " << key << '\n'; +#endif + const_iterator iter = begin(); + for (size_type i = 0; i != max_lv_; ++i) { + size_type focus = max_lv_ - 1 - i; + // invariant: iter->key <= key + while (not compare()(key, iter->forwards[focus]->key)) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "i: " << i << " focus: " << focus << ". " + << "since iter->forwards[focus]->key[" << iter->forwards[focus]->key + << "] <= key[" << key << "], "; +#endif + iter = iter->forwards[focus]; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "step forward iter to [" << iter->key << "]\n"; +#endif + } + // result: iter->key <= key < iter->forwards[focus]->key +#ifdef LIAM_UT_DEBUG_ + std::cerr << "The following fact holds at level " << focus + << ": iter->key[" << iter->key << "] <= key[" + << key << "] < iter->forwards[focus]->key[" << iter->forwards[focus]->key + <<"].\n"; +#endif + } + return iter; + } + std::vector find_predecessors(const hash_type& key, const size_type& lv) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "Keys contained in the list: "; + for (auto node : cont_) { + std::cerr << node.key << ' '; + } + std::cerr << '\n'; + std::cerr << "Target key: " << key << '\n'; +#endif + std::vector res; + res.resize(lv + 1); + iterator iter = begin(); + for (size_type i = 0; i != max_lv_; ++i) { + size_type focus = max_lv_ - 1 - i; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "i: " << i << " focus: " << focus << ".\n"; +#endif + // invariant: iter->key < key + while (compare()(iter->forwards[focus]->key, key)) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "since iter->forwards[focus]->key[" << iter->forwards[focus]->key + << "] < key[" << key << "], "; +#endif + iter = iter->forwards[focus]; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "step forward iter to [" << iter->key << "]\n"; +#endif + } + // result: iter->key < key <= iter->forwards[focus]->key +#ifdef LIAM_UT_DEBUG_ + std::cerr << "The following fact holds at level " << focus + << ": iter->key[" << iter->key << "] < key[" << key + << "] <= iter->forwards[focus]->key[" << iter->forwards[focus]->key + <<"].\n"; +#endif + if (focus < lv + 1) { + res[focus] = iter; +#ifdef LIAM_UT_DEBUG_ + std::cerr << "predecessor at level [" << focus + << "] has been recorded, while level upper limit is " << lv <<".\n"; +#endif + } + } + return res; + } + + public: + size_type size() const { + return cont_.size() - 2; + } + bool empty() const { + return size() == 0; + } + iterator begin() { + return cont_.begin(); + } + const_iterator begin() const { + return cont_.cbegin(); + } + const_iterator cbegin() const { + return cont_.cbegin(); + } + iterator end() { + return cont_.end(); + } + const_iterator end() const { + return cont_.cend(); + } + const_iterator cend() const { + return cont_.cend(); + } + void grow(const size_type new_max_lv) { + if (max_lv_ < new_max_lv) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "grow from [" << max_lv_ << "] to [" + << new_max_lv << "]!\n"; +#endif + max_lv_ = new_max_lv; + + iterator tail = std::prev(cont_.end()); + auto beg_tail = tail->forwards.end(); + tail->forwards.resize(max_lv_, cont_.end()); + + iterator head = cont_.begin(); + auto beg_head = head->forwards.end(); + head->forwards.resize(max_lv_, tail); + + return; + } else { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "abandon growing!\n"; +#endif + return; + } + } + void grow() { + grow(Factor * max_lv_); + } + size_type capability() const { + return std::pow(Factor, max_lv_); + } + + public: + const_iterator find(const value_type& target) const { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "finding [" << target << "]!\n"; +#endif + const hash_type key = hasher()(target); + const_iterator iter = find_helper(key); + return (iter->key == key) ? iter : cont_.end(); + } + void insert(const value_type& target) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "inserting [" << target << "]!\n"; +#endif + if (size() > static_cast(Factor - 1) / Factor * capability()) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "size[" << size() << "], Factor[" << Factor << "], capability[" << capability() << "]!\n"; +#endif + grow(); + } + const hash_type key = hasher()(target); + const size_type lv = rl_(); + std::vector predecessors = find_predecessors(key, lv); + if (predecessors[0]->forwards[0]->key == key) { // key already in skiplist +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] already in the skiplist, insert directly!\n"; +#endif + predecessors[0]->forwards[0]->values.insert(target); + return; + } else { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] not in the skiplist, insert a new node!\n"; +#endif + node_type node(key); + node.forwards.resize(lv + 1); + node.values.insert(target); + iterator inserted = cont_.insert(predecessors[0]->forwards[0], std::move(node)); + for (size_type i = 0; i != lv + 1; ++i) { + inserted->forwards[i] = predecessors[i]->forwards[i]; + predecessors[i]->forwards[i] = inserted; + } +#ifdef LIAM_UT_DEBUG_ + assert(inserted->forwards[0] == std::next(inserted)); +#endif + return; + } + } + void erase(const value_type& target, + const erase_policy policy = erase_policy::ALL) { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "erasing [" << target << "]!\n"; +#endif + const hash_type key = hasher()(target); + std::vector predecessors = find_predecessors(key, max_lv_); + if (predecessors[0]->forwards[0]->key == key) { // hit +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] is in the skiplist!\n"; +#endif + iterator found = predecessors[0]->forwards[0]; + for (auto iter = found->values.begin(); iter != found->values.end(); ) { + if (policy == erase_policy::ALL) { + if (*iter == target) { + iter = found->values.erase(iter); + } else { + ++iter; + } + } else if (policy == erase_policy::SINGLE) { + if (*iter == target) { + found->values.erase(iter); + break; + } + } + } +#ifdef LIAM_UT_DEBUG_ + std::cerr << "target(s) removed!\n"; +#endif + if (found->values.empty()) { + const size_type lvp1 = found->forwards.size(); // lv plus 1 + for (size_type i = 0; i != lvp1; ++i) { + predecessors[i]->forwards[i] = found->forwards[i]; + } + cont_.erase(found); +#ifdef LIAM_UT_DEBUG_ + std::cerr << "empty node removed!\n"; +#endif + return; + } else { + return; + } + } else { +#ifdef LIAM_UT_DEBUG_ + std::cerr << "key [" << key << "] is not in the skiplist, do nothing!\n"; +#endif + return; + } + } +}; + +#endif // SKIPLIST_SKIPLIST_TR_HPP_ diff --git a/c-cpp/17_skiplist/skiplist_tr_test.cc b/c-cpp/17_skiplist/skiplist_tr_test.cc new file mode 100644 index 00000000..15e3fa2d --- /dev/null +++ b/c-cpp/17_skiplist/skiplist_tr_test.cc @@ -0,0 +1,107 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/10/30. + */ +#include + +#include +#include +#include + +#include "skiplist_tr.hpp" + +int main() { + // 1. UT for skiplist_detail::random_level + skiplist_detail::random_level rl(5, 0.5); + std::map hist; + for (size_t i = 0; i != 10000; ++i) { + ++hist[rl()]; + } + for (auto p : hist) { + std::cout << p.first << ' ' << std::string(p.second / 100, '*') << '\n'; + } + + // 2. UT for skiplist(), init_internally(), size(), empty() + skiplist sl_default; + assert(sl_default.empty()); + // 2.1. UT for grow with abandon + sl_default.grow(1); + assert(sl_default.capability() == 4); + // 2.2. UT for grow + sl_default.grow(10); + assert(sl_default.capability() == 1024); + + // 3. UT for constructor of initializer_list and InputIt, init_internally(), insert(), + // find_predecessors() + skiplist sl{"hello", "world", "!"}; + assert(not sl.empty()); + assert(3 == sl.size()); + + // 4. UT for find() find_helper() + auto search = sl_default.find("nonexist"); + assert(search == sl_default.cend()); + assert(search == sl_default.end()); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + search = sl.find("nonexist"); + assert(search == sl.cend()); + assert(search == sl.end()); + + // 5. UT for insert(), find_predecessors() + // 5.1. UT for insert a already-exist item + sl.insert("hello"); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + // 5.2. UT for insert a new incoming item + search = sl.find("now exist"); + assert(search == sl.cend()); + assert(search == sl.end()); + sl.insert("now exist"); + search = sl.find("now exist"); + assert(search != sl.cend()); + assert(search != sl.end()); + + // 6. UT for erase(), find_predecessors() + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + // 6.1. UT for erase single item + sl.erase("hello", erase_policy::SINGLE); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + // 6.2. UT for erase all items + sl.erase("hello", erase_policy::ALL); + search = sl.find("hello"); + assert(search == sl.cend()); + assert(search == sl.end()); + // 6.3 UT for erase non-exist item + sl.erase("nonexist"); + + // 7. UT for insert() behind erase() + // 7.1. different word + sl.insert("world"); + search = sl.find("world"); + assert(search != sl.cend()); + assert(search != sl.end()); + assert(search->values.count("world") == 2); + // 7.1. same word, also UT for grow() + search = sl.find("hello"); + assert(search == sl.cend()); + assert(search == sl.end()); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + sl.insert("hello"); + search = sl.find("hello"); + assert(search != sl.cend()); + assert(search != sl.end()); + assert(search->values.count("hello") == 5); + + return 0; + // 8. UT for ~skiplist() +} + diff --git a/c-cpp/18_hashtable/.gitkeep b/c-cpp/18_hashtable/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/c-cpp/18_hashtable/hash_map.cc b/c-cpp/18_hashtable/hash_map.cc new file mode 100644 index 00000000..c2ae8dda --- /dev/null +++ b/c-cpp/18_hashtable/hash_map.cc @@ -0,0 +1,98 @@ +/** + * Created by Liam Huang (Liam0205) on 2018/08/14. + * This is an old test file for hash_map, created by Liam. + * Just for showing the inner logic for hash_map class template. + * Original posted on: + * https://github.com/Liam0205/leetcode/tree/master/met/hash_map.cc + */ + +#include +#include +#include +#include +#include + +template > +class hash_map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using size_type = size_t; + using hasher = std::hash; + + private: // helper + using wrapper = std::shared_ptr; + + public: // constructor + hash_map() { + container_.resize(primes_[size_level_]); + } + + public: // capacity + bool empty() const { return empty_; } + size_type size() const { return size_; } + size_type max_size() const { return primes_[size_level_]; } + + public: // find and modify + mapped_type& operator[](const key_type& key) { + auto hashed = find_hash(key); + if (not(container_[hashed]) and construct_new_on_position(hashed, key) and + load_factor() > max_load_factor()) { + expand(); + } + return container_[hashed]->second; + } + + public: // hash policy + double load_factor() const { return static_cast(size()) / max_size(); } + double max_load_factor() const { return max_load_factor_; } + void expand() const { + ++size_level_; + std::vector temp; + temp.resize(primes_[size_level_]); + for (auto w : container_) { + if (nullptr != w) { + auto hashed = find_hash(w->first); + temp[hashed] = w; + } + } + container_ = std::move(temp); + } + + private: // helper functions + size_type find_hash(const key_type& key) const { + const size_t csz = container_.size(); + size_t count = 0; + size_t hashed = hasher_(key) % csz; + while (nullptr != container_[hashed] and container_[hashed]->first != key) { + hashed = (hashed + ++count) % csz; + } + return hashed; + } + bool construct_new_on_position(const size_type pos, const key_type& key) { + empty_ = false; + ++size_; + container_[pos] = std::make_shared(std::make_pair(key, mapped_type())); + return true; + } + + private: + const hasher hasher_ = hasher(); + mutable size_t size_level_ = 0; + mutable std::vector container_; + static const size_t primes_[]; + bool empty_ = true; + size_type size_ = 0; + double max_load_factor_ = 0.75; +}; +template +const size_t hash_map::primes_[] = {7, 17, 29, 53, 101, 211, 401, 809, 1601, 3203}; // ... + +int main() { + hash_map test; + test[1]; + test[2] = 2; + std::cout << test[1] << ' ' << test[2] << std::endl; + return 0; +} diff --git a/c-cpp/18_hashtable/hashtable.c b/c-cpp/18_hashtable/hashtable.c new file mode 100644 index 00000000..03f7035a --- /dev/null +++ b/c-cpp/18_hashtable/hashtable.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include + +/* One implementation of hash table with linear probing. */ + +#define HASH_SHIFT 4 +#define HASH_SIZE (1 << HASH_SHIFT) +#define HASH_MASK (HASH_SIZE - 1) + +struct hash_table { + unsigned int used; + unsigned long entry[HASH_SIZE]; +}; + +void hash_table_reset(struct hash_table *table) +{ + int i; + + table->used = 0; + for (i = 0; i < HASH_SIZE; i++) + table->entry[i] = ~0; +} + +unsigned int hash_function(unsigned long value) +{ + return value & HASH_MASK; +} + +void dump_hash_table(struct hash_table *table) +{ + int i; + + for (i = 0; i < HASH_SIZE; i++) { + if (table->entry[i] == ~0) + printf("%2u: nil \n", i); + else + printf("%2u:%10lu -> %2u\n", + i, table->entry[i], + hash_function(table->entry[i])); + } +} + +void hash_function_test() +{ + int i; + + srandom(time(NULL)); + + for (i = 0; i < 10; i++) { + unsigned long val = random(); + printf("%10lu -> %2u\n", val, hash_function(val));; + } +} + +unsigned int next_probe(unsigned int prev_key) +{ + return (prev_key + 1) & HASH_MASK; +} + +void next_probe_test() +{ + int i; + unsigned int key1, key2; + + key1 = 0; + for (i = 0; i < HASH_SIZE; i++) { + key2 = next_probe(key1); + printf("%2u -> %2u\n", key1, key2); + key1 = key2; + } +} + +void hash_table_add(struct hash_table *table, unsigned long value) +{ + unsigned int key = hash_function(value); + + if (table->used >= HASH_SIZE) + return; + + while (table->entry[key] != ~0) + key = next_probe(key); + + table->entry[key] = value; + table->used++; +} + +unsigned int hash_table_slot(struct hash_table *table, unsigned long value) +{ + int i; + unsigned int key = hash_function(value); + + for (i = 0; i < HASH_SIZE; i++) { + if (table->entry[key] == value || table->entry[key] == ~0) + break; + key = next_probe(key); + } + + return key; +} + +bool hash_table_find(struct hash_table *table, unsigned long value) +{ + return table->entry[hash_table_slot(table, value)] == value; +} + +void hash_table_del(struct hash_table *table, unsigned long value) +{ + unsigned int i, j, k; + + if (!hash_table_find(table, value)) + return; + + i = j = hash_table_slot(table, value); + + while (true) { + table->entry[i] = ~0; + + do { + j = next_probe(j); + if (table->entry[j] == ~0) + return; + k = hash_function(table->entry[j]); + } while ((i <= j) ? (i < k && k <= j) : (i < k || k <= j)); + + table->entry[i] = table->entry[j]; + i = j; + } + table->used++; +} + +void hash_table_add_test() +{ + struct hash_table table; + + hash_table_reset(&table); + hash_table_add(&table, 87645); + + printf("Table has%s 87645\n", + hash_table_find(&table, 87645) ? "":"n't"); + printf("Table has%s 87647\n", + hash_table_find(&table, 87647) ? "":"n't"); +} + +void hash_table_del_test1() +{ + struct hash_table table; + + hash_table_reset(&table); + hash_table_add(&table, 0x1ff0); + hash_table_add(&table, 0x2ff0); + hash_table_add(&table, 0x3ff0); + dump_hash_table(&table); + + printf("=== Remove 0x1ff0\n"); + hash_table_del(&table, 0x1ff0); + dump_hash_table(&table); +} + +void hash_table_del_test2() +{ + struct hash_table table; + + hash_table_reset(&table); + hash_table_add(&table, 0x1ff0); + hash_table_add(&table, 0x1ff1); + hash_table_add(&table, 0x1ff2); + hash_table_add(&table, 0x2ff0); + dump_hash_table(&table); + + printf("=== Remove 0x1ff0\n"); + hash_table_del(&table, 0x1ff0); + dump_hash_table(&table); +} + +int main() +{ + //hash_function_test(); + //next_probe_test(); + //hash_table_add_test(); + hash_table_del_test2(); + + return 0; +} diff --git a/c-cpp/18_hashtable/listhash/listhash.c b/c-cpp/18_hashtable/listhash/listhash.c new file mode 100644 index 00000000..395590c9 --- /dev/null +++ b/c-cpp/18_hashtable/listhash/listhash.c @@ -0,0 +1,364 @@ +/************************************************************************* + > File Name: listhash.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-07 + > Desc: + ************************************************************************/ +#include +#include +#include +#include +#include"listhash.h" + +#ifdef MEMORY_TEST +#include +#endif + +hashtab * hashtab_create(int size,hash_key_func hash_value, + keycmp_func keycmp,hash_node_free_func hash_node_free) +{ + hashtab * h = NULL; + int i = 0; + + if ((size < 0) || (hash_value == NULL) || (keycmp == NULL)) + { + return NULL; + } + + h = (hashtab *)malloc(sizeof(hashtab)); + if (h == NULL) + { + return NULL; + } + + h->htables = (hashtab_node **)malloc(size * sizeof(hashtab_node*)); + if (h->htables == NULL) + { + return NULL; + } + + h->size = size; + h->nel = 0; + h->hash_value = hash_value; + h->keycmp = keycmp; + h->hash_node_free = hash_node_free; + + for (i = 0; i < size; i++) + { + h->htables[i] = NULL; + } + + return h; +} + +void hashtab_destory(hashtab *h) +{ + int i = 0; + hashtab_node * cur = NULL; + hashtab_node * tmp = NULL; + + if (h == NULL) + { + return; + } + + for (i = 0; i size; i++) + { + cur = h->htables[i]; + while (cur != NULL) + { + tmp = cur; + cur = cur->next; + h->hash_node_free(tmp); + } + h->htables[i] = NULL; + } + + free(h->htables); + free(h); + return; +} + +int hashtab_insert(hashtab * h,void *key,void *data) +{ + unsigned int hvalue = 0; + int i = 0; + hashtab_node *cur = NULL; + hashtab_node *prev = NULL; + hashtab_node *newnode = NULL; + + if ((h == NULL) || (key == NULL) || (data == NULL)) + { + return 1; + } + + /*获取hash 数值*/ + hvalue = h->hash_value(h,key); + cur = h->htables[hvalue]; + + /*hash桶中元素是从小到大排列的,找到要插入的位置*/ + while((cur != NULL) && (h->keycmp(h,key,cur->key) > 0)) + { + prev = cur; + cur = cur->next; + } + + /*如果key和当前key比对一致,直接返回,数据已经存在*/ + if ((cur != NULL) && (h->keycmp(h,key,cur->key) == 0)) + { + return 2; + } + + newnode = (hashtab_node *)malloc(sizeof(hashtab_node)); + if (newnode == NULL) + { + return 3; + } + + newnode->key = key; + newnode->data = data; + if (prev == NULL) + { + newnode->next = h->htables[hvalue]; + h->htables[hvalue] = newnode; + } + else + { + newnode->next = prev->next; + prev->next = newnode; + } + + h->nel++; + return 0; +} + +hashtab_node *hashtab_delete(hashtab *h, void *key) +{ + int hvalue = 0; + int i = 0; + hashtab_node *cur = NULL; + hashtab_node *prev = NULL; + + if ((h == NULL) || (key == NULL)) + { + return NULL; + } + + /*获取hash 数值*/ + hvalue = h->hash_value(h,key); + cur = h->htables[hvalue]; + /*hash桶中元素是从小到大排列的,找到要插入的位置*/ + while((cur != NULL) && (h->keycmp(h,key,cur->key) >= 0)) + { + if (h->keycmp(h,key,cur->key) == 0) + { + if (prev == NULL) + { + h->htables[hvalue] = cur->next; + } + else + { + prev->next = cur->next; + } + return cur; + } + prev = cur; + cur = cur->next; + } + + return NULL; +} + +void *hashtab_search(hashtab*h,void *key) +{ + int hvalue = 0; + int i = 0; + hashtab_node *cur = NULL; + + if ((h == NULL) || (key == NULL)) + { + return NULL; + } + + /*获取hash 数值*/ + hvalue = h->hash_value(h,key); + cur = h->htables[hvalue]; + /*hash桶中元素是从小到大排列的,找到要插入的位置*/ + while((cur != NULL) && (h->keycmp(h,key,cur->key) >= 0)) + { + if (h->keycmp(h,key,cur->key) == 0) + { + return cur->data; + } + cur = cur->next; + } + + return NULL; +} + +void hashtab_dump(hashtab *h) +{ + int i = 0; + hashtab_node * cur = NULL; + + if (h == NULL) + { + return ; + } + + printf("\r\n----开始--size[%d],nel[%d]------------",h->size,h->nel); + for( i = 0; i < h->size; i ++) + { + printf("\r\n htables[%d]:",i); + cur = h->htables[i]; + while((cur != NULL)) + { + printf("key[%s],data[%s] ",cur->key,cur->data); + cur = cur->next; + } + } + + printf("\r\n----结束--size[%d],nel[%d]------------",h->size,h->nel); +} + +struct test_node +{ + char key[80]; + char data[80]; +}; + +unsigned int siample_hash(const char *str) +{ + register unsigned int hash = 0; + register unsigned int seed = 131; + + while(*str) + { + hash = hash*seed + *str++; + } + + return hash & (0x7FFFFFFF); +} + +int hashtab_hvalue(hashtab *h,const void *key) +{ + return (siample_hash(key) % h->size); +} + +int hashtab_keycmp(hashtab *h,const void *key1,const void *key2) +{ + return strcmp(key1,key2); +} + +void hashtab_node_free(hashtab_node*node) +{ + struct test_node * ptmp = NULL; + + ptmp = container(node->key,struct test_node,key); + + free(ptmp); + free(node); +} + +int main () +{ + + int i = 0; + int res = 0; + char *pres = NULL; + hashtab_node * node = NULL; + struct test_node *p = NULL; + hashtab *h = NULL; + #ifdef MEMORY_TEST + setenv("MALLOC_TRACE","1.txt",1); + mtrace(); + #endif + + h = hashtab_create(5,hashtab_hvalue,hashtab_keycmp,hashtab_node_free); + assert(h!= NULL); + while(1) + { + p = (struct test_node*)malloc(sizeof(struct test_node)); + assert(p != NULL); + printf("\r\n 请输入key 和value,当可以等于\"quit\"时退出"); + scanf("%s",p->key); + scanf("%s",p->data); + + if(strcmp(p->key,"quit") == 0) + { + free(p); + break; + } + + res = hashtab_insert(h,p->key,p->data); + if (res != 0) + { + free(p); + printf("\r\n key[%s],data[%s] insert failed %d",p->key,p->data,res); + } + else + { + printf("\r\n key[%s],data[%s] insert success %d",p->key,p->data,res); + } + } + + hashtab_dump(h); + + while(1) + { + p = (struct test_node*)malloc(sizeof(struct test_node)); + assert(p != NULL); + printf("\r\n 请输入key 查询value的数值,当可以等于\"quit\"时退出"); + scanf("%s",p->key); + + if(strcmp(p->key,"quit") == 0) + { + free(p); + break; + } + pres = hashtab_search(h,p->key); + if (pres == NULL) + { + printf("\r\n key[%s] search data failed",p->key); + } + else + { + printf("\r\n key[%s],search data[%s] success",p->key,pres); + } + free(p); + } + hashtab_dump(h); + while(1) + { + p = (struct test_node*)malloc(sizeof(struct test_node)); + assert(p != NULL); + printf("\r\n 请输入key 删除节点的数值,当可以等于\"quit\"时退出"); + scanf("%s",p->key); + + if(strcmp(p->key,"quit") == 0) + { + free(p); + break; + } + node = hashtab_delete(h,p->key); + if (node == NULL) + { + printf("\r\n key[%s] delete node failed ",p->key); + } + else + { + printf("\r\n key[%s],delete data[%s] success",node->key,node->data); + h->hash_node_free(node); + } + free(p); + hashtab_dump(h); + } + + hashtab_destory(h); + #ifdef MEMORY_TEST + muntrace(); + #endif + return 0; + +} diff --git a/c-cpp/18_hashtable/listhash/listhash.h b/c-cpp/18_hashtable/listhash/listhash.h new file mode 100644 index 00000000..2e42adab --- /dev/null +++ b/c-cpp/18_hashtable/listhash/listhash.h @@ -0,0 +1,53 @@ +/************************************************************************* + > File Name: listhash.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-06 + > Desc: 根据linux内核模块hashtab编写用户层hashtab接口 + linux-4.19.1\security\selinux\ss\hashtab.c + linux-4.19.1\security\selinux\ss\hashtab.h + ************************************************************************/ + + +#ifndef __HASHTAB_H__ +#define __HASHTAB_H__ + + +typedef struct _hashtab_node +{ + void * key; + void * data; + struct _hashtab_node *next; +}hashtab_node; + +typedef struct _hashtab +{ + hashtab_node **htables; /*哈希桶*/ + int size; /*哈希桶的最大数量*/ + int nel; /*哈希桶中元素的个数*/ + int (*hash_value)(struct _hashtab *h,const void *key); /*哈希函数*/ + int (*keycmp)(struct _hashtab *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ + void (*hash_node_free)(hashtab_node *node); +}hashtab; + + +#define HASHTAB_MAX_NODES (0xffffffff) + +typedef int (*hash_key_func)(struct _hashtab *h,const void *key); /*哈希函数*/ +typedef int (*keycmp_func)(struct _hashtab *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ +typedef void (*hash_node_free_func)(hashtab_node *node); +/*根据当前结构体元素的地址,获取到结构体首地址*/ +#define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define container(ptr,type,member) ({\ + const typeof( ((type *)0)->member) *__mptr = (ptr);\ + (type *) ( (char *)__mptr - offsetof(type,member));}) + + +hashtab * hashtab_create(int size,hash_key_func hash_value, + keycmp_func keycmp,hash_node_free_func hash_node_free); +void hashtab_destory(hashtab *h); +int hashtab_insert(hashtab * h,void *key,void *data); +hashtab_node *hashtab_delete(hashtab *h, void *key); +void *hashtab_search(hashtab*h,void *key); + +#endif diff --git a/c-cpp/19_Dlisthash/Dlist.h b/c-cpp/19_Dlisthash/Dlist.h new file mode 100755 index 00000000..a95d2f3e --- /dev/null +++ b/c-cpp/19_Dlisthash/Dlist.h @@ -0,0 +1,114 @@ +/************************************************************************* + > File Name: Dlist.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-08 + > Desc: linux内核源码双向链表实现include/linux/list.h + ************************************************************************/ +#ifndef _LIST_HEAD_H +#define _LIST_HEAD_H + +// 双向链表节点 +struct list_head { + struct list_head *next, *prev; +}; + +// 初始化节点:设置name节点的前继节点和后继节点都是指向name本身。 +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +// 定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。 +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +// 初始化节点:将list节点的前继节点和后继节点都是指向list本身。 +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +// 添加节点:将new插入到prev和next之间。 +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +// 添加new节点:将new添加到head之后,是new称为head的后继节点。 +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +// 添加new节点:将new添加到head之前,即将new添加到双链表的末尾。 +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +// 从双链表中删除entry节点。 +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +// 从双链表中删除entry节点。 +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +// 从双链表中删除entry节点。 +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +// 从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。 +static inline void list_del_init(struct list_head *entry) +{ + __list_del_entry(entry); + INIT_LIST_HEAD(entry); +} + +// 用new节点取代old节点 +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +// 双链表是否为空 +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +// 获取"MEMBER成员"在"结构体TYPE"中的位置偏移 +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +// 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针 +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +// 遍历双向链表 +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#endif diff --git a/c-cpp/19_Dlisthash/LinkedHashMap.c b/c-cpp/19_Dlisthash/LinkedHashMap.c new file mode 100755 index 00000000..e1ec1828 --- /dev/null +++ b/c-cpp/19_Dlisthash/LinkedHashMap.c @@ -0,0 +1,398 @@ +/************************************************************************* + > File Name: LinkedHashMap.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-08 + > Desc: + ************************************************************************/ +#include +#include +#include +#include +#include "Dlist.h" +#include "LinkedHashMap.h" + + +LinkedHashMap *LinkedHashMap_Create(int size,int nel_max, + hash_value_func hash_value,keycmp_func keycmp, + hash_node_free_func hash_node_free) +{ + int i = 0; + LinkedHashMap *h = NULL; + + if ((size <= 0) || (hash_value == NULL) || (keycmp == NULL)) + { + return NULL; + } + + h = (LinkedHashMap *)malloc(sizeof(LinkedHashMap)); + if (h == NULL) + { + return NULL; + } + + h->hTabs = (LiskedHashMapNode**)malloc(sizeof(LiskedHashMapNode*) *size); + if (h->hTabs == NULL) + { + return NULL; + } + h->size = size; + h->nel = 0; + h->nel_max = nel_max; + h->hash_value = hash_value; + h->keycmp = keycmp; + h->hash_node_free = hash_node_free; + + for (i = 0; i < size; i++) + { + h->hTabs[i] = NULL; + } + + INIT_LIST_HEAD(&(h->header)); + + return h; +} + +void LinkedHashMap_destory(LinkedHashMap *h) +{ + struct list_head * pos = NULL; + struct list_head * next = NULL; + LiskedHashMapNode * ptmp = NULL; + + if (h == NULL) + { + return; + } + + list_for_each_safe(pos,next,&h->header) + { + ptmp = container_of(pos,LiskedHashMapNode,Dlist_node); + /*从双向链表中删除*/ + list_del_init(pos); + if (h->hash_node_free != NULL) + { + h->hash_node_free(ptmp,1); + } + } + + free(h->hTabs); + free(h); + + return; +} + +int LinkedHashMap_insert(LinkedHashMap *h,void *key,void *data) +{ + int i = 0; + int hPos = 0; + struct list_head *pos = NULL; + LiskedHashMapNode *cur = NULL; + LiskedHashMapNode *prev = NULL; + + hPos = h->hash_value(h,key); + cur = h->hTabs[hPos]; + while((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0)) + { + prev = cur; + cur = cur->next; + } + + if(cur == NULL) + { + /*链表节点满时,取表头节点,从当前哈希表和双向链表中都删除*/ + if(h->nel_max == h->nel) + { + cur = LinkedHashMap_delete(h,list_entry(h->header.next,LiskedHashMapNode,Dlist_node)->key); + + assert(cur != NULL); + /*释放节点key 和data的内容*/ + h->hash_node_free(cur,0); + } + else/*链表不满时,创建新的节点*/ + { + cur = (LiskedHashMapNode *)malloc(sizeof(LiskedHashMapNode)); + if (cur == NULL) + { + return 1; + + } + } + /*插入到hash桶中*/ + if(prev == NULL) + { + cur->next = h->hTabs[hPos]; + h->hTabs[hPos] = cur; + } + else + { + cur->next = prev->next; + prev->next= cur; + } + h->nel++; + } + else + { + /*从双向链表中删除*/ + list_del_init(&(cur->Dlist_node)); + /*只删除key 和data的内存*/ + h->hash_node_free(cur,0); + } + + /*赋值*/ + cur->key = key; + cur->data = data; + + + /*加的双向链表尾部*/ + list_add_tail(&(cur->Dlist_node),&(h->header)); + + return 0; +} + +LiskedHashMapNode * LinkedHashMap_delete(LinkedHashMap *h,void *key) +{ + int hPos = 0; + struct list_head *pos = NULL; + LiskedHashMapNode *cur = NULL; + LiskedHashMapNode *prev = NULL; + + /*查找当前节点*/ + hPos = h->hash_value(h,key); + cur = h->hTabs[hPos]; + while((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0)) + { + prev = cur; + cur = cur->next; + } + + if (cur == NULL) + { + return NULL; + } + + /*从哈希桶中删除*/ + if(prev == NULL) + { + h->hTabs[hPos] = cur->next; + } + else + { + prev->next = cur->next; + } + + /*从双向链表中删除*/ + list_del_init(&(cur->Dlist_node)); + h->nel--; + + return cur; +} + +void *LinkedHashMap_search(LinkedHashMap *h,void *key) +{ + int hPos = 0; + LiskedHashMapNode *cur = NULL; + + /*查找当前节点*/ + hPos = h->hash_value(h,key); + cur = h->hTabs[hPos]; + while((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0)) + { + cur = cur->next; + } + + if (cur == NULL) + { + return NULL; + } + + /*从双向链表中删除节点,加入尾部*/ + if (h->header.prev != &(cur->Dlist_node)) + { + list_del_init(&(cur->Dlist_node)); + list_add_tail(&(cur->Dlist_node),&(h->header)); + } + + return cur->data; +} + +void LinkedHashMap__dump(LinkedHashMap *h) +{ + int i = 0; + LiskedHashMapNode * cur = NULL; + struct list_head *pos = NULL; + + if (h == NULL) + { + return ; + } + + printf("\r\n----开始--size[%d],nel[%d]------------",h->size,h->nel); + for( i = 0; i < h->size; i ++) + { + printf("\r\n htables[%d]:",i); + cur = h->hTabs[i]; + while((cur != NULL)) + { + printf("key[%s],data[%s] ",cur->key,cur->data); + cur = cur->next; + } + } + + printf("\r\n--------------------------------------------------------\r\n"); + + list_for_each(pos,&(h->header)) + { + cur = list_entry(pos,LiskedHashMapNode,Dlist_node); + printf("key[%s] ",cur->key); + + } + + printf("\r\n----结束--size[%d],nel[%d]------------",h->size,h->nel); +} + + + + + +struct test_node +{ + char key[80]; + char data[80]; +}; + +unsigned int siample_hash(const char *str) +{ + register unsigned int hash = 0; + register unsigned int seed = 131; + + while(*str) + { + hash = hash*seed + *str++; + } + + return hash & (0x7FFFFFFF); +} + +int hashtab_hvalue(LinkedHashMap *h,const void *key) +{ + return (siample_hash(key) % h->size); +} + +int hashtab_keycmp(LinkedHashMap *h,const void *key1,const void *key2) +{ + return strcmp(key1,key2); +} + +void hashtab_node_free(LiskedHashMapNode *node,int flg) +{ + struct test_node * ptmp = NULL; + + ptmp = list_entry(node->key,struct test_node,key); + + free(ptmp); + if (flg) + { + free(node); + } +} + + +int main () +{ + + int i = 0; + int res = 0; + char *pres = NULL; + LiskedHashMapNode * node = NULL; + struct test_node *p = NULL; + LinkedHashMap *h = NULL; + setenv("MALLOC_TRACE","1.txt",1); + mtrace(); + + h = LinkedHashMap_Create(3,6,hashtab_hvalue,hashtab_keycmp,hashtab_node_free); + assert(h!= NULL); + while(1) + { + p = (struct test_node*)malloc(sizeof(struct test_node)); + assert(p != NULL); + printf("\r\n 请输入key 和value,当可以等于\"quit\"时退出"); + scanf("%s",p->key); + scanf("%s",p->data); + + if(strcmp(p->key,"quit") == 0) + { + free(p); + break; + } + + res = LinkedHashMap_insert(h,p->key,p->data); + if (res != 0) + { + free(p); + printf("\r\n key[%s],data[%s] insert failed %d",p->key,p->data,res); + } + else + { + printf("\r\n key[%s],data[%s] insert success %d",p->key,p->data,res); + } + LinkedHashMap__dump(h); + } + + + while(1) + { + p = (struct test_node*)malloc(sizeof(struct test_node)); + assert(p != NULL); + printf("\r\n 请输入key 查询value的数值,当可以等于\"quit\"时退出"); + scanf("%s",p->key); + + if(strcmp(p->key,"quit") == 0) + { + free(p); + break; + } + pres = LinkedHashMap_search(h,p->key); + if (pres == NULL) + { + printf("\r\n key[%s] search data failed",p->key); + } + else + { + printf("\r\n key[%s],search data[%s] success",p->key,pres); + } + free(p); + LinkedHashMap__dump(h); + } + + while(1) + { + p = (struct test_node*)malloc(sizeof(struct test_node)); + assert(p != NULL); + printf("\r\n 请输入key 删除节点的数值,当可以等于\"quit\"时退出"); + scanf("%s",p->key); + + if(strcmp(p->key,"quit") == 0) + { + free(p); + break; + } + node = LinkedHashMap_delete(h,p->key); + if (node == NULL) + { + printf("\r\n key[%s] delete node failed ",p->key); + } + else + { + printf("\r\n key[%s],delete data[%s] success",node->key,node->data); + h->hash_node_free(node,1); + } + free(p); + LinkedHashMap__dump(h); + } + + LinkedHashMap_destory(h); + muntrace(); + return 0; + +} + diff --git a/c-cpp/19_Dlisthash/LinkedHashMap.h b/c-cpp/19_Dlisthash/LinkedHashMap.h new file mode 100755 index 00000000..e61b8fcb --- /dev/null +++ b/c-cpp/19_Dlisthash/LinkedHashMap.h @@ -0,0 +1,40 @@ +/************************************************************************* + > File Name: LinkedHashMap.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-08 + > Desc: + ************************************************************************/ + + +#ifndef __LINKED_HASH_MAP__ +#define __LINKED_HASH_MAP__ + +/*数据存放节点*/ +typedef struct _lisked_hash_map_node +{ + void *key; /*键*/ + void *data; /*数据*/ZZ + struct _lisked_hash_map_node *next; /*哈希冲突时,用来挂接后续节点*/ + struct list_head Dlist_node;/*用来挂接双向链表*/ +}LiskedHashMapNode; + +typedef struct _lisked_hash_map +{ + LiskedHashMapNode **hTabs;/*哈希桶*/ + struct list_head header;/*双向循环链表头*/ + int size; /**/ + int nel_max; /*支持最大节点数*/ + int nel; /*当前节点数*/ + int (*hash_value)(struct _lisked_hash_map *h,const void *key); /*哈希函数*/ + int (*keycmp)(struct _lisked_hash_map *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ + void (*hash_node_free)(LiskedHashMapNode *node,int flg);/*用来释放节点内存*/ + +}LinkedHashMap; + +typedef int (*hash_value_func)(struct _lisked_hash_map *h,const void *key); /*哈希函数*/ +typedef int (*keycmp_func)(struct _lisked_hash_map *h,const void *key1,const void *key2);/*哈希key比较函数,当哈希数值一致时使用*/ +typedef void (*hash_node_free_func)(LiskedHashMapNode *node,int flg); + +LiskedHashMapNode * LinkedHashMap_delete(LinkedHashMap *h,void *key); +#endif diff --git a/c-cpp/23_binarytree/binarytree.c b/c-cpp/23_binarytree/binarytree.c new file mode 100644 index 00000000..4143cd17 --- /dev/null +++ b/c-cpp/23_binarytree/binarytree.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +/* Implement binary tree in array */ + +#define MAX_TREE_NODES (1 << 8) + +struct node { + int data; +}; + +struct binary_tree { + union { + unsigned long nodes; + struct node *n[MAX_TREE_NODES]; + }; +}; + +void init_binary_tree(struct binary_tree *tree) +{ + int i; + + for(i = 0; i < MAX_TREE_NODES; i++) { + tree->n[i] = NULL; + } +} + +struct node* create_node(int data) +{ + struct node* n; + + n = malloc(sizeof(struct node)); + + if (n) + n->data = data; + + return n; +} + +void fake_a_tree(struct binary_tree* tree) +{ + /* data is in ordered */ + int i, data[10] = {7, 4, 9, 2, 6, 8, 10, 1, 3, 5}; + + init_binary_tree(tree); + + /* root start at 1 */ + for (i = 0; i < 10; i++) + tree->n[i+1] = create_node(data[i]); + + tree->nodes = 10; +} + +void _in_order(struct binary_tree* tree, int index) +{ + if (!tree->n[index]) + return; + + /* left child at (index << 1) */ + _in_order(tree, index << 1); + + printf("[%2d]: %4d\n", index, tree->n[index]->data); + + /* right child at (index << 1) + 1 */ + _in_order(tree, (index << 1) + 1); +} + +void in_order(struct binary_tree* tree) +{ + _in_order(tree, 1); +} + +int main() +{ + struct binary_tree tree; + + fake_a_tree(&tree); + in_order(&tree); + return 0; +} diff --git a/c-cpp/23_binarytree/tree/binarytree.c b/c-cpp/23_binarytree/tree/binarytree.c new file mode 100644 index 00000000..8a5e66b9 --- /dev/null +++ b/c-cpp/23_binarytree/tree/binarytree.c @@ -0,0 +1,213 @@ +/************************************************************************* + > File Name: binarytree.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-12 + > Desc: + ************************************************************************/ +#include + +#include +#include +#include +#include"list_queue.h" + +typedef struct _treenode +{ + int data; + struct _treenode *lchild; + struct _treenode *rchild; +}Tnode,Tree; + +void binarytree_create(Tree **Root) +{ + int a = 0; + printf("\r\n输入节点数值((当输入为100时,当前节点创建完成))):"); + scanf("%d",&a); + + + if (a == 100) + { + *Root = NULL; + } + else + { + *Root = (Tnode *)malloc(sizeof(Tnode)); + if (*Root == NULL) + { + return; + } + + (*Root)->data = a; + printf("\r\n create %d 的左孩子:",a); + binarytree_create(&((*Root)->lchild)); + printf("\r\n create %d 的右孩子:",a); + binarytree_create(&((*Root)->rchild)); + } + + return ; +} + +void binarytree_destory(Tree *root) +{ + if (root == NULL) + { + return; + } + + binarytree_destory(root->lchild); + binarytree_destory(root->rchild); + free(root); +} + +/*先序遍历:根结点--》左子树---》右子树*/ +void binarytree_preorder(Tree *root) +{ + if (root == NULL) + { + return; + } + printf(" %d ",root->data); + binarytree_preorder(root->lchild); + binarytree_preorder(root->rchild); + return; +} +/*中序遍历:左子树--》跟节点---》右子树*/ +void binarytree_inorder(Tree *root) +{ + if (root == NULL) + { + return; + } + binarytree_inorder(root->lchild); + printf(" %d ",root->data); + binarytree_inorder(root->rchild); + return; +} +/*后序遍历:左子树---》右子树-》根节点*/ +void binarytree_postorder(Tree *root) +{ + if (root == NULL) + { + return; + } + binarytree_postorder(root->lchild); + binarytree_postorder(root->rchild); + printf(" %d ",root->data); + return; +} + +void binarytree_levelorder(Tree * root) +{ + list_queue *queue = NULL; + Tnode * node = NULL; + + if(root == NULL) + { + return; + } + + queue = list_queue_create(); + + /*根节点先入队*/ + list_queue_enqueue(queue,(void *)root); + + while(!list_queue_is_empty(queue)) + { + list_queue_dequeue(queue,(void *)&node); + printf(" %d ",node->data); + + if(node->lchild != NULL) + { + list_queue_enqueue(queue,(void *)node->lchild); + } + + if(node->rchild != NULL) + { + list_queue_enqueue(queue,(void *)node->rchild); + } + } + + free(queue); + +} +/*打印叶子节点*/ +void binarytree_printfleaf(Tree *root) +{ + if (root == NULL) + { + return; + } + + if ((root->lchild == NULL) && (root->rchild == NULL)) + { + printf(" %d ",root->data); + } + else + { + binarytree_printfleaf(root->lchild); + binarytree_printfleaf(root->rchild); + } +} +/*打印叶子的个数*/ +int binarytree_getleafnum(Tree*root) +{ + if (root == NULL) + { + return 0; + } + + if ((root->lchild == NULL) && (root->rchild == NULL)) + { + return 1; + } + + return binarytree_getleafnum(root->lchild) + binarytree_getleafnum(root->rchild); + +} +/*打印数的高度*/ +int binarytree_gethigh(Tree *root) +{ + int lhigh = 0; + int rhigh = 0; + + if (root == NULL) + { + return 0; + } + + lhigh = binarytree_gethigh(root->lchild); + rhigh = binarytree_gethigh(root->rchild); + + return ((lhigh > rhigh)?(lhigh + 1):(rhigh + 1)); +} + +int main() +{ + Tree *root = NULL; + + setenv("MALLOC_TRACE","1.txt",1); + mtrace(); + + printf("\r\n创建二叉树:"); + binarytree_create(&root); + printf("\r\n先序遍历二叉树:"); + binarytree_preorder(root); + printf("\r\n中序遍历二叉树:"); + binarytree_inorder(root); + printf("\r\n后序遍历二叉树:"); + binarytree_postorder(root); + printf("\r\n层次遍历二叉树:"); + binarytree_levelorder(root); + + printf("\r\n打印二叉树叶子节点:"); + binarytree_printfleaf(root); + printf("\r\n打印二叉树叶子节点个数:%d",binarytree_getleafnum(root)); + printf("\r\n打印二叉树高度:%d",binarytree_gethigh(root)); + + binarytree_destory(root); + + muntrace(); + return 0; +} + diff --git a/c-cpp/23_binarytree/tree/list_queue.c b/c-cpp/23_binarytree/tree/list_queue.c new file mode 100644 index 00000000..2dbc9f91 --- /dev/null +++ b/c-cpp/23_binarytree/tree/list_queue.c @@ -0,0 +1,85 @@ +/************************************************************************* + > File Name: list_queue.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-13 + > Desc: + ************************************************************************/ +#include +#include +#include +#include"./list_queue.h" + +/*创建队列头*/ +list_queue *list_queue_create() +{ + list_queue * queue = NULL; + + queue = (list_queue *)malloc(sizeof(list_queue)); + if(queue == NULL) + { + return NULL; + } + + queue->num = 0; + queue->head = NULL; + queue->tail = NULL; + + return queue; +} +int list_queue_enqueue(list_queue *queue,void *data) +{ + queue_node *ptmp = NULL; + + if(queue == NULL) + { + return -1; + } + + ptmp = (queue_node *)malloc(sizeof(queue_node)); + if (ptmp == NULL) + { + return -1; + } + + ptmp->data = data; + ptmp->next = NULL; + if (queue->head == NULL) + { + queue->head = ptmp; + } + else + { + queue->tail->next = ptmp; + + } + queue->tail = ptmp; + queue->num++; + + return 0; +} + +/*出队*/ +int list_queue_dequeue(list_queue *queue,void **data) +{ + queue_node * ptmp = NULL; + + if ((queue == NULL) || (data == NULL) || list_queue_is_empty(queue)) + { + return -1; + } + + *data = queue->head->data; + ptmp = queue->head; + queue->head = queue->head->next; + queue->num--; + + if (queue->head == NULL) + { + queue->tail = NULL; + } + + + free(ptmp); + return 0; +} diff --git a/c-cpp/23_binarytree/tree/list_queue.h b/c-cpp/23_binarytree/tree/list_queue.h new file mode 100644 index 00000000..a6991855 --- /dev/null +++ b/c-cpp/23_binarytree/tree/list_queue.h @@ -0,0 +1,30 @@ +/************************************************************************* + > File Name: list_queue.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-10-13 + > Desc: + ************************************************************************/ + +#ifndef LINK_LIST_QUEUE_H +#define LINK_LIST_QUEUE_H + +typedef struct _list_queue_node +{ + void *data; + struct _list_queue_node *next; +}queue_node; + +typedef struct _list_queue +{ + int num; + queue_node *head; + queue_node *tail; +}list_queue; + +#define list_queue_is_empty(queue) ((queue->num) == 0) +list_queue *list_queue_create(); +int list_queue_enqueue(list_queue *queue,void *data); +int list_queue_dequeue(list_queue *queue,void **data); + +#endif diff --git a/c-cpp/24_binarysearchtree/binary_search_tree.cpp b/c-cpp/24_binarysearchtree/binary_search_tree.cpp new file mode 100644 index 00000000..4a43e38a --- /dev/null +++ b/c-cpp/24_binarysearchtree/binary_search_tree.cpp @@ -0,0 +1,291 @@ +/* + * Filename: /home/zwk/code/data_structrue/c++/tree/binary_search_tree/main.cpp + * Path: /home/zwk/code/data_structrue/c++/tree/binary_search_tree + * Created Date: Wednesday, May 8th 2019, 11:04:48 pm + * Author: zwk + * + * refer to https://time.geekbang.org/column/article/68334 + */ + +#include + +using namespace std; + +typedef int DataType; + +struct treeNode +{ + DataType data; + treeNode *left = nullptr; + treeNode *right = nullptr; +}; + +class binarySearchTree +{ +private: + treeNode *root; + int num; // tree node numbers +public: + binarySearchTree() : num(0) + { + root = new treeNode; + root->left = nullptr; + root->right = nullptr; + } + + bool find(DataType it, treeNode *root) + { + if (nullptr == root) + return false; + if (it == root->data) { + return true; + } else if (it > root->data) { + return find(it, root->right); + } else { + return find(it, root->left); + } + } + + bool find_data(DataType it) + { + return find(it, root); + /* + treeNode* p = root; + while (p != nullptr) + { + if (it < p->data)p = p->left; + else if (it>p->data)p = p->right; + else return true; + } + return false; + */ + } + + DataType get_max() + { + if (nullptr == root) + return NULL; + treeNode *tmp = root; + while (tmp->right != nullptr) { + tmp = tmp->right; + } + return tmp->data; + } + + DataType get_min() + { + if (nullptr == root) + return NULL; + treeNode *tmp = root; + while (tmp->left != nullptr) { + tmp = tmp->left; + } + return tmp->data; + } + + void insert_data(DataType it) + // 利用二分查找的思想,借助树的结构使用递归 + { + if (0 == num) { + root->data = it; + num++; + return; + } + treeNode *p = root; + while (p != nullptr) { + if (it < p->data) { + if (nullptr == p->left) { + p->left = new treeNode; + p->left->data = it; + num++; + return; + } + p = p->left; + } else { + if (nullptr == p->right) { + p->right = new treeNode; + p->right->data = it; + num++; + return; + } + p = p->right; + } + } + } + + DataType get_prenode(DataType it) + { + if (nullptr == root) + return NULL; + if (it == root->data) + return NULL; + treeNode *p = root; + treeNode *pp = nullptr; + while (p != nullptr) { + if (p->data < it) { + pp = p; // label parent root + p = p->right; + + } else if (p->data > it) { + pp = p; // label parent root + p = p->left; + } else { + + break; + } + } + return ((nullptr == p) ? NULL : pp->data); + } + + DataType get_postnode(DataType it) + { + if (nullptr == root) + return -1; + treeNode *p = root; + while (p != nullptr) { + if (p->data < it) { + p = p->right; + } else if (p->data > it) { + p = p->left; + } else { + break; + } + } + if (nullptr == p) { + return -1; + } else if (p->left != nullptr) { + return p->left->data; + } else if (p->right != nullptr) { + return p->right->data; + } else { + return NULL; + } + } + + void mid_order(treeNode *rt) + { + if (nullptr == rt) + return; + mid_order(rt->left); + cout << rt->data << '\t'; + mid_order(rt->right); + } + + void order() + { + if (nullptr == root) + return; + return mid_order(root); + } + + int get_high(treeNode *rt) + { + int lhigh = 0; + int rhigh = 0; + if (nullptr == rt) + return 0; + lhigh = get_high(rt->left); + rhigh = get_high(rt->right); + return ((lhigh > rhigh) ? (lhigh + 1) : (rhigh + 1)); + } + + int high() + { + if (nullptr == root) + return 1; + return get_high(root); + } + + void delet(DataType it) + { + if (NULL == root) + return; + treeNode *p = root; + treeNode *pp = NULL; //pp记录的是p的父节点 + while (p != NULL && p->data != it) { + pp = p; + if (it > p->data) + p = p->right; + else + p = p->left; + } + if (p == NULL) + return; //没有找到 + //删除的节点有两个子节点 + if (p->left != NULL && p->right != NULL) { + treeNode *minP = p->right; + treeNode *minPP = p; //记录P的父节点 + while (minP->left != NULL) //寻找右子树最小节点 + { + minPP = minP; + minP = minP->left; + } + // 注意这里,非常巧妙的办法。只是换值。“换汤不换药” + // 用后继节点替换到要删除节点的位置。 然后就变成删除后继节点的问题了。为了逻辑统一 代码书写简洁。我们把后继节点赋给了p + p->data = minP->data; //将minP的值替换到p中 + //将p换到叶节点上,使用叶节点方法进行删除, 而且最小节点肯定没有左节点,叶节点删除方法参见后面的代码。 + p = minP; + pp = minPP; + } + + //删除节点是叶节点或者是仅有一个节点 + treeNode *child; + if (p->left != NULL) + child = p->left; + else if (p->right != NULL) + child = p->right; + else + child = NULL; + + if (NULL == pp) + root = child; //删除的是根节点 + else if (p == pp->left) + pp->left = child; + else + pp->right = child; + } +}; + +int main() +{ + binarySearchTree my_tree; + + // must input in the order of layers + my_tree.insert_data(33); + my_tree.insert_data(16); + my_tree.insert_data(50); + my_tree.insert_data(13); + my_tree.insert_data(18); + my_tree.insert_data(34); + my_tree.insert_data(58); + my_tree.insert_data(15); + my_tree.insert_data(17); + my_tree.insert_data(25); + my_tree.insert_data(51); + my_tree.insert_data(66); + my_tree.insert_data(19); + my_tree.insert_data(27); + my_tree.insert_data(55); + + if (my_tree.find_data(25)) { + cout << "找到了数字25" << endl; + } else { + cout << "没有找到数字25" << endl; + } + my_tree.delet(13); + my_tree.delet(18); + my_tree.delet(55); + cout << "Max: " << my_tree.get_max() << endl; + cout << "Min: " << my_tree.get_min() << endl; + cout << "pre node of 17 is " << my_tree.get_prenode(17) << endl; + cout << "pre node of 51 is " << my_tree.get_prenode(51) << endl; + cout << "pre node of 33 is " << my_tree.get_prenode(33) << endl; + + cout << "post node of 19 is " << my_tree.get_postnode(19) << endl; + cout << "post node of 25 is " << my_tree.get_postnode(25) << endl; + cout << "post node of 58 is " << my_tree.get_postnode(58) << endl; + cout << "post node of 58 is " << my_tree.get_postnode(51) << endl; + my_tree.order(); + cout << "high of tree is " << my_tree.high() << endl; + return 0; +} diff --git a/c-cpp/24_binarysearchtree/binarysearchtree.c b/c-cpp/24_binarysearchtree/binarysearchtree.c new file mode 100644 index 00000000..f069de39 --- /dev/null +++ b/c-cpp/24_binarysearchtree/binarysearchtree.c @@ -0,0 +1,322 @@ +/************************************************************************* + > File Name: binarysearchtree.c + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-12 + > Desc: + ************************************************************************/ +#include +#include +#include +#include +#include"binarysearchtree.h" + + +bstree *bstree_create(compare_fuc compare,destory_fuc destory) +{ + bstree *tree = NULL; + + tree = (bstree*)malloc(sizeof(bstree)); + if (tree == NULL) + { + return NULL; + } + + tree->size = 0; + tree->compare = compare; + tree->destory = destory; + tree->root = NULL; + return tree; +} + +bstree_node *bstree_search(bstree *tree,mytype data) +{ + bstree_node *node = NULL; + int res = 0; + + if ((tree == NULL) || (bstree_is_empty(tree))) + { + return NULL; + } + node = tree->root; + + while(node != NULL) + { + res = tree->compare(data,node->data); + if(res == 0) + { + return node; + } + else if (res > 0) + { + node = node->rchild; + } + else + { + node = node->lchild; + } + } + + return NULL; +} + +int bstree_insert(bstree * tree, mytype data) +{ + bstree_node *node = NULL; + bstree_node *tmp = NULL; + int res = 0; + + if (tree == NULL) + { + return -1; + } + + node = (bstree_node *)malloc(sizeof(bstree_node)); + if (node == NULL) + { + return -2; + } + + node->data = data; + node->lchild = NULL; + node->rchild = NULL; + + /*如果二叉树为空,直接挂到根节点*/ + if (bstree_is_empty(tree)) + { + tree->root = node; + tree->size++; + return 0; + } + + tmp = tree->root; + + while(tmp != NULL) + { + res = tree->compare(data,tmp->data); + if (res > 0) /*去右孩子查找*/ + { + if (tmp->rchild == NULL) + { + tmp->rchild = node; + tree->size++; + return 0; + } + tmp = tmp->rchild; + } + else /*去左孩子查找*/ + { + if(tmp->lchild == NULL) + { + tmp->lchild = node; + tree->size++; + return 0; + } + tmp = tmp->lchild; + } + } + + return -3; +} + +int bstree_delete(bstree *tree,mytype data) +{ + bstree_node *node = NULL;/*要删除的节点*/ + bstree_node *pnode = NULL;/*要删除节点的父节点*/ + bstree_node *minnode = NULL;/*要删除节点的父节点*/ + bstree_node *pminnode = NULL;/*要删除节点的父节点*/ + mytype tmp = 0; + int res = 0; + + if ((tree == NULL) || (bstree_is_empty(tree))) + { + return -1; + } + + node = tree->root; + while ((node != NULL) && ((res = tree->compare(data,node->data)) != 0)) + { + pnode = node; + if(res > 0) + { + node = node->rchild; + } + else + { + node = node->lchild; + } + } + /*说明要删除的节点不存在*/ + if (node == NULL) + { + return -2; + } + + /*1、如果要删除node有2个子节点,需要找到右子树的最小节点minnode, + * 更新minnode和node节点数据,这样minnode节点就是要删除的节点 + * 再更新node和pnode节点指向要删除的节点*/ + if ((node->lchild != NULL) && (node->rchild != NULL)) + { + minnode = node->rchild; + pminnode = node; + + while(minnode->lchild != NULL) + { + pminnode = minnode; + minnode = minnode->lchild; + } + + /*node 节点和minnode节点数据互换*/ + tmp = node->data; + node->data = minnode->data; + minnode->data = tmp; + /*更新要删除的节点和其父节点*/ + node = minnode; + pnode = pminnode; + } + + /*2、当前要删除的节点只有左孩子或者右孩子时,直接父节点的直向删除的节点*/ + if (node->lchild != NULL) + { + minnode = node->lchild; + } + else if (node->rchild != NULL) + { + minnode = node->rchild; + } + else + { + minnode = NULL; + } + + if (pnode == NULL)/*当要删除的时根节点时,*/ + { + tree->root = minnode; + } + else if (pnode->lchild == node) + { + pnode->lchild = minnode; + } + else + { + pnode->rchild = minnode; + } + tree->size--; + free (node); + + return 0; +} + +/*采用递归方式删除节点*/ +void bstree_destory_node(bstree *tree,bstree_node *root) +{ + if (root == NULL) + { + return; + } + + bstree_destory_node(tree,root->lchild); + bstree_destory_node(tree,root->rchild); + free(root); +} + +/*二叉搜索树销毁*/ +void bstree_destory(bstree *tree) +{ + bstree_destory_node(tree,tree->root); + free(tree); + return; +} + +/*中序遍历打印树节点*/ +void bstree_inorder_node(bstree_node *root) +{ + bstree_node *node = NULL; + if (root == NULL) + { + return; + } + + bstree_inorder_node(root->lchild); + printf(" %d ",root->data); + bstree_inorder_node(root->rchild); + return; +} + +void bstree_dump(bstree *tree) +{ + bstree_node *node = NULL; + if ((tree == NULL) || (bstree_is_empty(tree))) + { + printf("\r\n 当前树是空树"); + } + printf("\r\nSTART-----------------%d------------\r\n",tree->size); + bstree_inorder_node(tree->root); + printf("\r\nEND---------------------------------",tree->size); + +} + +int bstree_compare(mytype key1,mytype key2) +{ + if (key1 == key2) + { + return 0; + } + else if (key1 > key2) + { + return 1; + } + else + { + return -1; + } +} + +int main() +{ + bstree *tree = NULL; + bstree_node *node = NULL; + mytype data = 0; + int res = 0; + + setenv("MALLOC_TRACE","1.txt",1); + mtrace(); + + tree = bstree_create(bstree_compare,NULL); + assert(tree != NULL); + + while(1) + { + printf("\r\n插入一个数字,输入100时退出:"); + scanf("%d",&data); + if(data == 100)break; + res = bstree_insert(tree,data); + printf("\r\n %d 插入%s成功",data,(res != 0)?("不"):(" ")); + } + bstree_dump(tree); + + while(1) + { + printf("\r\n查询一个数字,输入100时退出:"); + scanf("%d",&data); + if(data == 100)break; + node = bstree_search(tree,data); + printf("\r\n %d %s存在树中",data,(node == NULL)?("不"):(" ")); + } + bstree_dump(tree); + while(1) + { + printf("\r\n删除一个数字,输入100时退出:"); + scanf("%d",&data); + if(data == 100)break; + res = bstree_delete(tree,data); + printf("\r\n %d 删除%s成功",data,(res != 0)?("不"):(" ")); + bstree_dump(tree); + } + + bstree_destory(tree); + + muntrace(); + + return 0; +} diff --git a/c-cpp/24_binarysearchtree/binarysearchtree.h b/c-cpp/24_binarysearchtree/binarysearchtree.h new file mode 100644 index 00000000..b4e06119 --- /dev/null +++ b/c-cpp/24_binarysearchtree/binarysearchtree.h @@ -0,0 +1,34 @@ +/************************************************************************* + > File Name: binarysearchtree.h + > Author: jinshaohui + > Mail: jinshaohui789@163.com + > Time: 18-11-12 + > Desc: + ************************************************************************/ +#ifndef __BINARY_SEARCH_TREE__ +#define __BINARY_SEARCH_TREE__ +typedef int mytype; + +typedef struct _bstree_node +{ + mytype data; + struct _bstree_node *lchild; + struct _bstree_node *rchild; +}bstree_node; + +typedef struct _bstree +{ + int size; + int (*compare)(mytype key1,mytype key2); + int (*destory)(mytype data); + bstree_node *root; +}bstree; + +typedef int (*compare_fuc)(mytype key1,mytype key2); +typedef int (*destory_fuc)(mytype data); + +#define bstree_is_empty(tree) (tree->size == 0) + +bstree *bstree_create(compare_fuc compare,destory_fuc destory); + +#endif diff --git a/c-cpp/24_binarysearchtree/bst.c b/c-cpp/24_binarysearchtree/bst.c new file mode 100644 index 00000000..7fdfd877 --- /dev/null +++ b/c-cpp/24_binarysearchtree/bst.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include + +enum child_dir { + left_child, + right_child, + root, +}; + +struct node { + unsigned long data; + struct node *left; + struct node *right; +}; + +struct root { + struct node *r; +}; + +void dump(struct node *node, int level, enum child_dir dir) +{ + if (!node) + return; + + dump(node->right, level + 1, right_child); + + if (dir == left_child) + printf("%*s\n", level*3, "|"); + + printf("%*s - %05lu\n", level*3, " ", node->data); + + if (dir == right_child) + printf("%*s\n", level*3, "|"); + + dump(node->left, level + 1, left_child); +} + +struct node* find(struct root *root, unsigned long data) +{ + struct node* n = root->r; + + while (n) { + if (n->data == data) + return n; + if (data < n->data) + n = n->left; + else + n = n->right; + } + + return NULL; +} + +struct node* new_node(unsigned long data) +{ + struct node *n; + + n = malloc(sizeof(struct node)); + + n->data = data; + n->left = n->right = NULL; + return n; +} + +void insert(struct root *root, struct node *new) +{ + struct node *parent; + + if (!root->r) { + root->r = new; + return; + } + + parent = root->r; + + while (true) { + /* Don't support duplicate data */ + if (new->data == parent->data) + break; + + if (new->data < parent->data) { + if (!parent->left) { + parent->left = new; + break; + } + parent = parent->left; + } else { + if (!parent->right) { + parent->right = new; + break; + } + parent = parent->right; + } + } +} + +struct node* delete(struct root *root, unsigned long data) +{ + struct node *n = root->r, **p = &root->r; + struct node *child; + + while (n && n->data != data) { + if (data < n->data) { + p = &n->left; + n = n->left; + } else { + p = &n->right; + n = n->right; + } + } + + if (!n) + return NULL; + + if (n->left && n->right) { + struct node *rn = n->right, **rp = &n->right; + + while (rn->left) { + rp = &rn->left; + rn = rn->left; + } + + n->data = rn->data; + n = rn; + p = rp; + } + + child = n->left ? n->left : n->right; + *p = child; + + return NULL; +} + +void insert_test() +{ + struct root tree; + struct node* n; + + tree.r = NULL; + + insert(&tree, new_node(9)); + + insert(&tree, new_node(5)); + insert(&tree, new_node(2)); + insert(&tree, new_node(8)); + + insert(&tree, new_node(18)); + insert(&tree, new_node(13)); + insert(&tree, new_node(21)); + insert(&tree, new_node(20)); + + dump(tree.r, 0, root); + + n = find(&tree, 18); + if (n && n->data == 18) + printf("Get 18\n"); + +} + +void delete_test() +{ + struct root tree; + struct node* n; + + tree.r = NULL; + + insert(&tree, new_node(9)); + + insert(&tree, new_node(5)); + insert(&tree, new_node(2)); + insert(&tree, new_node(8)); + + insert(&tree, new_node(18)); + insert(&tree, new_node(13)); + insert(&tree, new_node(21)); + insert(&tree, new_node(20)); + + dump(tree.r, 0, root); + + delete(&tree, 20); + printf("Delete 20\n"); + dump(tree.r, 0, root); + + delete(&tree, 9); + printf("Delete 9\n"); + dump(tree.r, 0, root); +} + +int main() +{ + //insert_test(); + delete_test(); + return 0; +} diff --git a/c-cpp/24_tree/Trie.c b/c-cpp/24_tree/Trie.c new file mode 100644 index 00000000..1a8f7f9e --- /dev/null +++ b/c-cpp/24_tree/Trie.c @@ -0,0 +1,91 @@ +/************************************************************************* + > Author: Liu Zhang + > Mail: lz-850610@163.com + > Time: 2019-05-10 + > Desc: 字典树实现 + ************************************************************************/ +#include +#include +#include + +#define OK 1 +#define ERROR 0 +#define TRUE 1 +#define FALSE 0 + +typedef int Status; + +typedef struct Node { + char data; + struct Node *children[26]; + Status end; +} Trie, *TriePtr; + +void Init(TriePtr *T) +{ + (*T) = (TriePtr)malloc(sizeof(Trie)); + (*T)->data = '/'; + (*T)->end = FALSE; +} + +void Insert(TriePtr T, char *str) { + + int index; + char c; + + while(c = *str++) + { + index = c - 'a'; + if (T->children[index] == NULL) + { + TriePtr Node; + Node = (TriePtr)malloc(sizeof(Trie)); + Node->data = c; + Node->end = FALSE; + T->children[index] = Node; + } + + T = T->children[index]; + } + + T->end = TRUE; +} + + +Status Search(TriePtr T, char *str) { + + int index; + char c; + + while(c = *str++) + { + index = c - 'a'; + if (T->children[index] == NULL) + { + return FALSE; + } + + T = T->children[index]; + } + + if (T->end) { + return TRUE; + } else { + return FALSE; + } +} + + +int main(int argc, char const *argv[]) +{ + TriePtr T; + Init(&T); + char *str = "hello"; + char *str2 = "hi"; + + Insert(T, str); + + printf("str is search %d\n", Search(T, str)); + printf("str2 is search %d\n", Search(T, str2)); + return 0; +} \ No newline at end of file diff --git a/c-cpp/24_tree/binarysearchtree.c b/c-cpp/24_tree/binarysearchtree.c new file mode 100644 index 00000000..8b6420a7 --- /dev/null +++ b/c-cpp/24_tree/binarysearchtree.c @@ -0,0 +1,222 @@ +/************************************************************************* + > Author: Liu Zhang + > Mail: lz-850610@163.com + > Time: 2019-05-10 + > Desc: 二叉搜索树实现 + ************************************************************************/ +#include +#include +#include + +#define OK 1 +#define ERROR 0 +#define TRUE 1 +#define FALSE 0 +typedef int Status; +typedef char ElemType; + +typedef struct node { + ElemType data; + struct node *lchild, *rchild; +} BTree, *BTreePtr; + +/**************** 插入 **********************/ +Status Insert(BTreePtr *T, ElemType e) { + + BTreePtr p; + + if (*T == NULL) { + *T = (BTreePtr)malloc(sizeof(BTree)); + (*T)->data = e; + + return TRUE; + } else { + p = *T; + while ( p != NULL) { + + if (e > p->data) { + + if (p->rchild == NULL) { + p->rchild = (BTreePtr) malloc (sizeof(BTree)); + p->rchild->data = e; + return TRUE; + } + p = p->rchild; + } else { + + if (p->lchild == NULL) + { + p->lchild = (BTreePtr) malloc (sizeof(BTree)); + p->lchild->data = e; + return TRUE; + } + p = p->lchild; + } + } + } + + return FALSE; +} + +/**************** 删除 **********************/ +Status Delete(BTreePtr T, ElemType e) { + BTreePtr p, pp, minP, minPP, child; + child = NULL; + p = T; + pp = NULL; + + while ( (p != NULL) && (p->data != e) ) { + pp = p; + + if (e > p->data) { + p = p->rchild; + } else { + p = p->lchild; + } + } + + if (p == NULL) return FALSE; + + //双节点 + if ((p->lchild != NULL) && (p->rchild != NULL)) + { + minPP = p; + minP = p->rchild; + + while (minP->lchild != NULL) { + minPP = minP; + minP = minP->lchild; + } + p->data = minP->data; + minPP->lchild = minP->rchild; + free(minP); + + return TRUE; + } + + //有一个节点 + if ((p->lchild != NULL) || (p->rchild != NULL)) { //应该将原有的pp同child连接在一起 + + if (p->lchild) { + child = p->lchild; + } else { + child = p->rchild; + } + if(pp->data>p->data) + { + pp->lchild=child; + } else + { + pp->rchild=child; + } + free(p); + return TRUE; + } + + //没有节点 + if (pp->lchild == p) {//这里面临pp除p以外的节点为null的情况 + pp->lchild = child; + } else { + pp->rchild = child; + } + + return TRUE; +} + +/**************** 查找 **********************/ + +Status Find(BTreePtr T, ElemType e) { + + if (T == NULL) return FALSE; + + while ((T != NULL) && (T->data != e)) { + + if (e > T->data) { + T = T->rchild; + } else { + T = T->lchild; + } + } + + if (T) { + return TRUE; + } else { + return FALSE; + } +} + + +/**************** 最大值 **********************/ +ElemType FindMax(BTreePtr T) { + ElemType max; + + while(T != NULL) { + max = T->data; + T = T->rchild; + } + return max; +} + + +/**************** 最小值 **********************/ +ElemType FindMin(BTreePtr T) { + ElemType min; + + while(T != NULL) { + min = T->data; + T = T->lchild; + } + return min; +} + + +void PreOrderTraverse(BTreePtr T)//前序遍历二叉树 +{ + if (T == NULL) return; + + if(T) + { + printf("%d ",T->data); + PreOrderTraverse(T->lchild); + PreOrderTraverse(T->rchild); + } +} + + +void DestroyTree(BTreePtr T) { + if (T) + { + if (T->lchild) + { + DestroyTree(T->lchild); + } + + if(T->rchild) + { + DestroyTree(T->rchild); + } + + free(T); + T = NULL; + } +} + +/***************** 执行测试 *************************/ +int main(int argc, char const *argv[]) +{ + BTreePtr T; + T = NULL; + int a[] = {33, 16, 50, 13, 18, 34, 58, 15, 17, 25, 51, 66, 19, 27, 55}; + int i; + for (i = 0; i < 15; i++) { + Insert(&T, a[i]); + } + printf("Max is %d\n", FindMax(T)); + printf("Min is %d\n", FindMin(T)); + Delete(T, 18); + Delete(T, 13); + PreOrderTraverse(T); + DestroyTree(T); + + return 0; +} diff --git a/c-cpp/28_heap/heap.c b/c-cpp/28_heap/heap.c new file mode 100644 index 00000000..6bd872df --- /dev/null +++ b/c-cpp/28_heap/heap.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + +/* Implement heap */ + +#define MAX_HEAP_SIZE (1 << 8) + +struct element { + int data; +}; + +struct heap { + union { + unsigned long elements; + struct element *elem[MAX_HEAP_SIZE]; + }; +}; + +void init_heap(struct heap *heap) +{ + int i; + + for(i = 0; i < MAX_HEAP_SIZE; i++) { + heap->elem[i] = NULL; + } +} + +void dump_heap(struct heap *heap, int index) +{ + struct element *elem; + int level; + + if (index > heap->elements) + return; + + elem = heap->elem[index]; + level = fls(index); + + dump_heap(heap, index * 2 + 1); + + if (!(index % 2) && index != 1) + printf("%*s\n", level*3, "|"); + + printf("%*s - %05d\n", level*3, " ", elem->data); + + if (index % 2 && index != 1) + printf("%*s\n", level*3, "|"); + + dump_heap(heap, index * 2); +} + +void dump(struct heap *heap, int elements) +{ + int i; + + for (i = 1; i <= elements; i++) + printf("[%02d]: %4d\n", i, heap->elem[i]->data); + +} + +struct element* create_element(int data) +{ + struct element *elem; + + elem = malloc(sizeof(struct element)); + + if (elem) + elem->data = data; + + return elem; +} + +void fake_a_heap(struct heap *heap) +{ + /* data is in ordered */ + int i, data[10] = {7, 4, 9, 2, 6, 8, 10, 1, 3, 5}; + + init_heap(heap); + + /* root start at 1 */ + for (i = 0; i < 10; i++) + heap->elem[i+1] = create_element(data[i]); + + heap->elements = 10; +} + +void swap(struct heap *heap, int i, int j) +{ + struct element *tmp; + + tmp = heap->elem[j]; + heap->elem[j] = heap->elem[i]; + heap->elem[i] = tmp; +} + +void heapify(struct heap *heap, int parent) +{ + struct element **elem = heap->elem; + int elements = heap->elements; + int left, right, max; + + while (true) { + left = parent * 2; + right = left + 1; + + max = parent; + if (left <= elements && elem[max]->data < elem[left]->data) + max = left; + if (right <= elements && elem[max]->data < elem[right]->data) + max = right; + + if (max == parent) + break; + + swap(heap, max, parent); + parent = max; + } +} + +void build_heap(struct heap *heap) +{ + int i; + + for (i = heap->elements / 2; i >= 1; i--) + heapify(heap, i); +} + +int heap_sort(struct heap *heap) +{ + int elements = heap->elements; + + while (heap->elements) { + swap(heap, 1, heap->elements); + heap->elements--; + heapify(heap, 1); + } + + return elements; +} + +int main() +{ + struct heap heap; + int elements; + + fake_a_heap(&heap); + dump_heap(&heap, 1); + + printf("After Heapify:\n"); + build_heap(&heap); + dump_heap(&heap, 1); + + printf("After Heap sort:\n"); + elements = heap_sort(&heap); + dump(&heap, elements); + return 0; +} diff --git a/c-cpp/30_Graph/graph.c b/c-cpp/30_Graph/graph.c new file mode 100644 index 00000000..0f00c7b6 --- /dev/null +++ b/c-cpp/30_Graph/graph.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include + +struct vertex; +struct vertex_adjs { + struct vertex *v; + struct vertex_adjs *next; +}; + +struct vertex { + int data; + struct vertex_adjs *adj; +}; + +#define MAX_GRAPH_VERTEX (1 << 8) +struct graph { + struct vertex *vxs[MAX_GRAPH_VERTEX]; +}; + +void init_graph(struct graph *graph) +{ + int i; + + for (i = 0; i < MAX_GRAPH_VERTEX; i++) + graph->vxs[i] = NULL; +} + +struct vertex *create_vertex(int data) +{ + struct vertex *v; + + v = malloc(sizeof(struct vertex)); + + if (v) { + v->data = data; + v->adj = NULL; + } + + return v; +} + +struct vertex_adjs *create_vertex_adj(struct vertex *v) +{ + struct vertex_adjs *v_adj; + + v_adj = malloc(sizeof(struct vertex_adjs)); + + if (!v_adj) + return NULL; + + v_adj->v = v; + v_adj->next = NULL; + return v_adj; +} + +void insert_adj(struct vertex *v, struct vertex *adj) +{ + struct vertex_adjs **v_adj; + + v_adj = &v->adj; + + while (*v_adj) + v_adj = &(*v_adj)->next; + + *v_adj = create_vertex_adj(adj); +} + +void dump_raw(struct graph *graph) +{ + int i; + + for (i = 0; i < MAX_GRAPH_VERTEX; i++) { + struct vertex *v = graph->vxs[i]; + struct vertex_adjs *adj; + if (v == NULL) + continue; + + printf("Vertex[%02d]: %8d ->", i, v->data); + + adj = v->adj; + while (adj) { + printf(" %8d,", adj->v->data); + adj = adj->next; + } + printf("\n"); + } +} + +/* + 1 ----- 2 ----- 3 + | / | / + | / | / + | / | / + | / | / + | / | / + 4 ----- 5 +*/ +void fake_a_graph(struct graph *graph) +{ + int i; + + init_graph(graph); + + for (i = 0; i < 5; i++) + graph->vxs[i] = create_vertex(i+1); + + /* connect 1 -> 2, 1 -> 4 */ + insert_adj(graph->vxs[0], graph->vxs[1]); + insert_adj(graph->vxs[0], graph->vxs[3]); + /* connect 2 -> 1, 2 -> 3, 2 -> 5, 2 -> 4 */ + insert_adj(graph->vxs[1], graph->vxs[0]); + insert_adj(graph->vxs[1], graph->vxs[2]); + insert_adj(graph->vxs[1], graph->vxs[4]); + insert_adj(graph->vxs[1], graph->vxs[3]); + /* connect 3 -> 2, 3 -> 5 */ + insert_adj(graph->vxs[2], graph->vxs[1]); + insert_adj(graph->vxs[2], graph->vxs[4]); + /* connect 4 -> 1, 4 -> 2, 4 -> 5 */ + insert_adj(graph->vxs[3], graph->vxs[0]); + insert_adj(graph->vxs[3], graph->vxs[1]); + insert_adj(graph->vxs[3], graph->vxs[4]); + /* connect 5 -> 4, 5 -> 2, 5 -> 3 */ + insert_adj(graph->vxs[4], graph->vxs[3]); + insert_adj(graph->vxs[4], graph->vxs[1]); + insert_adj(graph->vxs[4], graph->vxs[3]); +} + +int main() +{ + struct graph g; + + fake_a_graph(&g); + dump_raw(&g); + return 0; +} diff --git a/c-cpp/bst.c b/c-cpp/bst.c new file mode 100644 index 00000000..7fdfd877 --- /dev/null +++ b/c-cpp/bst.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include + +enum child_dir { + left_child, + right_child, + root, +}; + +struct node { + unsigned long data; + struct node *left; + struct node *right; +}; + +struct root { + struct node *r; +}; + +void dump(struct node *node, int level, enum child_dir dir) +{ + if (!node) + return; + + dump(node->right, level + 1, right_child); + + if (dir == left_child) + printf("%*s\n", level*3, "|"); + + printf("%*s - %05lu\n", level*3, " ", node->data); + + if (dir == right_child) + printf("%*s\n", level*3, "|"); + + dump(node->left, level + 1, left_child); +} + +struct node* find(struct root *root, unsigned long data) +{ + struct node* n = root->r; + + while (n) { + if (n->data == data) + return n; + if (data < n->data) + n = n->left; + else + n = n->right; + } + + return NULL; +} + +struct node* new_node(unsigned long data) +{ + struct node *n; + + n = malloc(sizeof(struct node)); + + n->data = data; + n->left = n->right = NULL; + return n; +} + +void insert(struct root *root, struct node *new) +{ + struct node *parent; + + if (!root->r) { + root->r = new; + return; + } + + parent = root->r; + + while (true) { + /* Don't support duplicate data */ + if (new->data == parent->data) + break; + + if (new->data < parent->data) { + if (!parent->left) { + parent->left = new; + break; + } + parent = parent->left; + } else { + if (!parent->right) { + parent->right = new; + break; + } + parent = parent->right; + } + } +} + +struct node* delete(struct root *root, unsigned long data) +{ + struct node *n = root->r, **p = &root->r; + struct node *child; + + while (n && n->data != data) { + if (data < n->data) { + p = &n->left; + n = n->left; + } else { + p = &n->right; + n = n->right; + } + } + + if (!n) + return NULL; + + if (n->left && n->right) { + struct node *rn = n->right, **rp = &n->right; + + while (rn->left) { + rp = &rn->left; + rn = rn->left; + } + + n->data = rn->data; + n = rn; + p = rp; + } + + child = n->left ? n->left : n->right; + *p = child; + + return NULL; +} + +void insert_test() +{ + struct root tree; + struct node* n; + + tree.r = NULL; + + insert(&tree, new_node(9)); + + insert(&tree, new_node(5)); + insert(&tree, new_node(2)); + insert(&tree, new_node(8)); + + insert(&tree, new_node(18)); + insert(&tree, new_node(13)); + insert(&tree, new_node(21)); + insert(&tree, new_node(20)); + + dump(tree.r, 0, root); + + n = find(&tree, 18); + if (n && n->data == 18) + printf("Get 18\n"); + +} + +void delete_test() +{ + struct root tree; + struct node* n; + + tree.r = NULL; + + insert(&tree, new_node(9)); + + insert(&tree, new_node(5)); + insert(&tree, new_node(2)); + insert(&tree, new_node(8)); + + insert(&tree, new_node(18)); + insert(&tree, new_node(13)); + insert(&tree, new_node(21)); + insert(&tree, new_node(20)); + + dump(tree.r, 0, root); + + delete(&tree, 20); + printf("Delete 20\n"); + dump(tree.r, 0, root); + + delete(&tree, 9); + printf("Delete 9\n"); + dump(tree.r, 0, root); +} + +int main() +{ + //insert_test(); + delete_test(); + return 0; +} diff --git a/csharp/05-array/Array.cs b/csharp/05-array/Array.cs new file mode 100644 index 00000000..3ceeaee2 --- /dev/null +++ b/csharp/05-array/Array.cs @@ -0,0 +1,116 @@ +using System; + +namespace algo05_array +{ + public sealed class Array where T : IComparable + { + private T[] _data; + private readonly int _capacity; + private int _length; + + public Array(int capacity) + { + _data = new T[capacity]; + _capacity = capacity; + _length = 0; + } + + // length of list + public int Length => _length; + + // insert a new element at specified index (index start from 0) + public void Insert(int index, T newElem) + { + if (_length == _capacity) + { + throw new OutOfMemoryException("List has no more space"); + } + + if (index < 0 || index > _length) + { + throw new IndexOutOfRangeException("Index was outside the bounds of the list"); + } + + // to loop array from end until finding the target index + for (int k = _length; k > index; k--) + { + _data[k] = _data[k - 1]; + } + + _data[index] = newElem; + + _length++; + } + + // get an element base on index + public T Find(int index) + { + if (index < 0 || index > _length - 1) + { + throw new IndexOutOfRangeException("Index was outside the bounds of the list"); + } + + return _data[index]; + } + + // search the node which matches specified value and return its index (index start from 0) + public int IndexOf(T val) + { + if (_length == 0) return -1; + if (_data[0].Equals(val)) return 0; + if (_data[_length - 1].CompareTo(val) == 0) return _length - 1; + + int start = 1; + while (start < _length - 1) + { + if (_data[start].CompareTo(val) == 0) return start; + start++; + } + + return -1; + } + + // delete an node which is on the specified index + public bool Delete(int index) + { + if (index < 0 || index > _length - 1) + { + throw new IndexOutOfRangeException("Index must be in the bound of list"); + } + + T deletedElem = _data[index]; + if (index < _length - 1) + { + for (int k = index; k < _length; k++) + { + _data[k] = _data[k + 1]; + } + } + + _length--; + + return true; + } + + // delete an node + public bool Delete(T val) + { + int index; + for (index = 0; index < Length; index++) + { + if (_data[index].CompareTo(val) == 0) break; + } + + if (index >= Length) return false; + + return Delete(index); + } + + // clear list + public void Clear() + { + _data = new T[_capacity]; + _length = 0; + } + } +} \ No newline at end of file diff --git a/csharp/05-array/algo05_array.csproj b/csharp/05-array/algo05_array.csproj new file mode 100644 index 00000000..70e59c99 --- /dev/null +++ b/csharp/05-array/algo05_array.csproj @@ -0,0 +1,8 @@ + + + + netcoreapp2.2 + false + + + diff --git a/csharp/06-linkedlist/LRUWithArray.cs b/csharp/06-linkedlist/LRUWithArray.cs new file mode 100644 index 00000000..cc733a7b --- /dev/null +++ b/csharp/06-linkedlist/LRUWithArray.cs @@ -0,0 +1,44 @@ +using algo05_array; + +namespace algo06_linked_list +{ + /// + /// 使用数组实现LRU缓存淘汰算法 + /// + public class LRUWithArray + { + private readonly int _capacity; + + public LRUWithArray(int capacity) + { + _capacity = capacity; + CachedList = new Array(capacity); + } + + public Array CachedList { get; } + + public void Set(int val) + { + // 找出该值在缓存中的索引位置 + int idx = CachedList.IndexOf(val); + + // 存在该缓存值 + if (idx != -1) + { + CachedList.Delete(idx); + CachedList.Insert(0, val); + return; + } + + // 不存在该缓存值 + if (CachedList.Length == _capacity) + { + // 缓存已满,删除最后一个元素 + CachedList.Delete(CachedList.Length - 1); + } + + // 将新缓存插入到表头 + CachedList.Insert(0, val); + } + } +} \ No newline at end of file diff --git a/csharp/06-linkedlist/LRUWithLinkedList.cs b/csharp/06-linkedlist/LRUWithLinkedList.cs new file mode 100644 index 00000000..28123525 --- /dev/null +++ b/csharp/06-linkedlist/LRUWithLinkedList.cs @@ -0,0 +1,48 @@ +namespace algo06_linked_list +{ + /// + /// 使用单链表实现LRU缓存淘汰算法 + /// + public class LRUWithLinkedList + { + private readonly int _capacity; + + /// + /// 构造函数 + /// + /// 缓存容量 + public LRUWithLinkedList(int capacity = 10) + { + _capacity = capacity; + } + + public SingleLinkedList CachedList { get; } = new SingleLinkedList(); + + /// + /// 存储缓存数据 + /// + /// + public void Set(int val) + { + // 尝试删除匹配到和给定值相等的结点,并返回 + var deletedNode = CachedList.Delete(value: val); + + // 数据在缓存中存在,从原位置删除,然后插入到表头 + if (deletedNode != null) + { + CachedList.Insert(1, val); + return; + } + + // 数据不存在 + if (CachedList.Length == _capacity) + { + // 链表已满,删除尾结点,将新数据插入到头部 + CachedList.Delete(CachedList.Length); + } + + // 将新缓存值插入到表头 + CachedList.Insert(1, val); + } + } +} \ No newline at end of file diff --git "a/csharp/06-linkedlist/LRU\347\274\223\345\255\230\345\256\236\347\216\260\346\200\235\350\267\257.txt" "b/csharp/06-linkedlist/LRU\347\274\223\345\255\230\345\256\236\347\216\260\346\200\235\350\267\257.txt" new file mode 100644 index 00000000..896706c0 --- /dev/null +++ "b/csharp/06-linkedlist/LRU\347\274\223\345\255\230\345\256\236\347\216\260\346\200\235\350\267\257.txt" @@ -0,0 +1,8 @@ +实现LRU缓存淘汰算法思路: + +维护一个有序单链表,越靠近链尾的数据是最早访问的。 +当有一个新的数据被访问时, +1. 如果数据在缓存中,则将其从原位置删除,然后插入到表头; +2. 如果数据不在缓存中,有两种情况: + 1) 链表未满,则将数据插入到表头; + 2) 链表已满,则删除尾结点,将新数据插入到表头。 diff --git a/csharp/06-linkedlist/SingleLinkedList.cs b/csharp/06-linkedlist/SingleLinkedList.cs new file mode 100644 index 00000000..cb0537d5 --- /dev/null +++ b/csharp/06-linkedlist/SingleLinkedList.cs @@ -0,0 +1,163 @@ +using System; + +namespace algo06_linked_list +{ + /// + /// 单链表的插入、删除、清空、查找 + /// + /// + public class SingleLinkedList where T : IComparable + { + public SingleLinkedList() + { + Head = new ListNode(default(T)); + } + + public SingleLinkedList(params T[] list) + { + Head = new ListNode(default(T)); + if (list == null) return; + + var p = Head; + foreach (var item in list) + { + var q = new ListNode(item); + p.Next = q; + p = q; + } + + Length = list.Length; + } + + // Head node + public ListNode First => Head.Next; + public ListNode Head { get; } + + public int Length { get; private set; } + + public ListNode Insert(int position, T newElem) + { + if (position < 1 || position > Length + 1) + { + throw new IndexOutOfRangeException("Position must be in bound of list"); + } + + var p = Head; + + int j = 1; + while (p != null && j < position) + { + p = p.Next; + ++j; + } + + var newNode = new ListNode(newElem); + newNode.Next = p.Next; + p.Next = newNode; + + Length++; + + return newNode; + } + + public ListNode Find(int position) + { + ListNode p = First; + int j = 1; + + while (p != null && j < position) + { + p = p.Next; + j++; + } + + if (p == null || j > position) + { + return null; + } + + return p; + } + + public ListNode Find(T elem) + { + ListNode p = Head.Next; + + while (p != null) + { + if (p.Value.CompareTo(elem) == 0) return p; + + p = p.Next; + } + + return null; + } + + public ListNode Delete(T value) + { + ListNode cur = Head; + while (cur.Next != null && cur.Next.Value.CompareTo(value) != 0) + { + cur = cur.Next; + } + + if (cur.Next == null) return null; + + var q = cur.Next; + cur.Next = q.Next; + + Length--; + + return q; + } + + public ListNode Delete(int position) + { + if (position < 1 || position > Length) + { + return null; + } + + var p = First; + int j = 1; + while (p != null && j < position - 1) + { + p = p.Next; + ++j; + } + + var q = p.Next; + p.Next = q.Next; + + Length--; + + return q; + } + + public void Clear() + { + var cur = Head; + while (cur.Next != null) + { + var q = cur.Next; + cur.Next = null; + + cur = q; + } + + Length = 0; + } + } + + public class ListNode + { + public ListNode(T value) + { + Value = value; + } + + public T Value { get; } + + public ListNode Next { get; set; } + } +} \ No newline at end of file diff --git a/csharp/06-linkedlist/algo06_linked_list.csproj b/csharp/06-linkedlist/algo06_linked_list.csproj new file mode 100644 index 00000000..0b68f2f8 --- /dev/null +++ b/csharp/06-linkedlist/algo06_linked_list.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp2.2 + + false + + + + + + + diff --git a/csharp/07-linkedlist/_07_linkedlist/SingleLinkedListAlgo.cs b/csharp/07-linkedlist/_07_linkedlist/SingleLinkedListAlgo.cs new file mode 100644 index 00000000..f0d3e49f --- /dev/null +++ b/csharp/07-linkedlist/_07_linkedlist/SingleLinkedListAlgo.cs @@ -0,0 +1,172 @@ +using System; +using algo06_linked_list; + +namespace algo07_linkedlist +{ + /// + /// 单链表常用算法操作 + /// 1. 链表反转 + /// 2. 环的检测 + /// 3. 两个有序链表的合并 + /// 4. 删除链表倒数第n个结点 + /// 5. 求链表的中间结点 + /// + /// + public class SingleLinkedListAlgo : SingleLinkedList where T : IComparable + { + public SingleLinkedListAlgo(params T[] list) : base(list) + { + } + + /// + /// 链表反转 + /// + public void Reverse() + { + if (Length <= 1) return; + + ListNode p = First; + ListNode q = First.Next; + + ListNode r = null; + + p.Next = null; + while (q != null) + { + r = q.Next; + + q.Next = p; + p = q; + q = r; + } + + Head.Next = p; + } + + /// + /// 环的检测 + /// + /// + /// 用快慢两个指针,快指针每次移动2个结点,慢指针每次移动1个结点,当两个指针相遇时,说明存在环。 + /// LeetCode 编号: 141 + /// + public bool HasCycle() + { + if (Length == 0) return false; + + var slow = Head.Next; + var fast = Head.Next.Next; + + while (fast != null && slow != null && fast != slow) + { + fast = fast.Next?.Next; + slow = slow.Next; + } + + bool ret = fast == slow; + return ret; + } + + /// + /// 合并两个有序链表(从小到大) + /// + /// LeetCode 编号: 21 + /// + /// + public SingleLinkedListAlgo Merge(SingleLinkedListAlgo listAlgo) + { + if (listAlgo == null) return null; + + var root = new SingleLinkedListAlgo(); + + ListNode pointer = root.Head; // 总是向新链表的尾结点 + + var head1 = listAlgo.First; + var head2 = this.First; + + while (head1 != null && head2 != null) + { + if (head1.Value.CompareTo(head2.Value) < 0) + { + pointer.Next = head1; + head1 = head1.Next; + } + else + { + pointer.Next = head2; + head2 = head2.Next; + } + + pointer = pointer.Next; // 指向尾结点 + } + + if (head1 != null) + { + pointer.Next = head1; + } + + if (head2 != null) + { + pointer.Next = head2; + } + + return root; + } + + /// + /// 删除倒数第n个结点 + /// + /// + /// 用快慢两个指针,快指针比慢指针早n个结点,然后再同步移动两个指针,当快指针指向尾结点时,慢指针就是将要删除的结点 + /// LeetCode 编号: 19 + /// + /// + public void RemoveNthNodeFromEnd(int n) + { + if (n < 1 || n > Length) return; + + ListNode preNode = Head; + ListNode curNode = Head; + + for (int i = 0; i < n; i++) + { + curNode = curNode.Next; + } + + if (curNode == null) return; + + while (curNode.Next != null) + { + preNode = preNode.Next; + curNode = curNode.Next; + } + + preNode.Next = preNode.Next.Next; + } + + /// + /// 链表的中间结点 + /// + /// + /// 思路: 利用快慢指针,快指针步长2,慢指针步长1,当快指针到达尾结点时,慢指针正好到达中间结点 + /// LeetCode 编号: 876 + /// + /// + public ListNode FindMiddleNode() + { + if (First?.Next == null) return null; + + ListNode slowPointer = First; + ListNode fastPointer = First.Next; + + while (fastPointer.Next?.Next != null) + { + fastPointer = fastPointer.Next.Next; + slowPointer = slowPointer.Next; + } + + slowPointer = slowPointer.Next; + return slowPointer; + } + } +} \ No newline at end of file diff --git a/csharp/07-linkedlist/_07_linkedlist/algo07_linkedlist.csproj b/csharp/07-linkedlist/_07_linkedlist/algo07_linkedlist.csproj new file mode 100644 index 00000000..0e988e29 --- /dev/null +++ b/csharp/07-linkedlist/_07_linkedlist/algo07_linkedlist.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.2 + + + + + + + diff --git a/csharp/08-stack/algo08_stack/ArrayStack.cs b/csharp/08-stack/algo08_stack/ArrayStack.cs new file mode 100644 index 00000000..c8b1e9f9 --- /dev/null +++ b/csharp/08-stack/algo08_stack/ArrayStack.cs @@ -0,0 +1,41 @@ +using System; + +namespace algo08_stack +{ + public class ArrayStack + { + private readonly int _capacity; + + private readonly T[] _data; + + private int _top = -1; // 指向栈顶元素,当为-1时表示栈为空 + + public ArrayStack(int capacity) + { + _capacity = capacity; + + _data = new T[capacity]; + } + + public int Count => _top + 1; + + public void Push(T val) + { + if (Count == _capacity) throw new InvalidOperationException("Stack full."); + + _top++; + + _data[_top] = val; + } + + public T Pop() + { + if (_top == -1) throw new InvalidOperationException("Stack empty."); + + T val = _data[_top]; + _top--; + + return val; + } + } +} \ No newline at end of file diff --git a/csharp/08-stack/algo08_stack/LinkedStack.cs b/csharp/08-stack/algo08_stack/LinkedStack.cs new file mode 100644 index 00000000..f0017702 --- /dev/null +++ b/csharp/08-stack/algo08_stack/LinkedStack.cs @@ -0,0 +1,51 @@ +using System; + +namespace algo08_stack +{ + public class LinkedStack + { + private StackListNode _top; + + public int Count { get; private set; } + + public void Push(T val) + { + var newNode = new StackListNode(val); + newNode.Next = _top; + _top = newNode; + + Count++; + } + + public T Pop() + { + if (_top == null) throw new InvalidOperationException("Stack empty"); + + T val = _top.Value; + _top = _top.Next; + + Count--; + + return val; + } + + public void Clear() + { + while (Count > 0) + { + Pop(); + } + } + } + + public class StackListNode + { + public StackListNode(T nodeValue) + { + Value = nodeValue; + } + + public T Value { get; set; } + public StackListNode Next { get; set; } + } +} \ No newline at end of file diff --git a/csharp/08-stack/algo08_stack/LinkedStackBrowser.cs b/csharp/08-stack/algo08_stack/LinkedStackBrowser.cs new file mode 100644 index 00000000..ad06ad04 --- /dev/null +++ b/csharp/08-stack/algo08_stack/LinkedStackBrowser.cs @@ -0,0 +1,39 @@ +namespace algo08_stack +{ + /// + /// 利用链栈实现浏览器怎么进后退 + /// + public class LinkedStackBrowser + { + private readonly LinkedStack _backStack = new LinkedStack(); + private readonly LinkedStack _forwardStack = new LinkedStack(); + + public void Open(string url) + { + _backStack.Push(url); + + _forwardStack.Clear(); + } + + public string Backward() + { + if (_backStack.Count == 0) return string.Empty; + + string url = _backStack.Pop(); + + _forwardStack.Push(url); + + return url; + } + + public string Forward() + { + if (_forwardStack.Count == 0) return string.Empty; + + string url = _forwardStack.Pop(); + _backStack.Push(url); + + return url; + } + } +} \ No newline at end of file diff --git a/csharp/08-stack/algo08_stack/algo08_stack.csproj b/csharp/08-stack/algo08_stack/algo08_stack.csproj new file mode 100644 index 00000000..ec0b0b9c --- /dev/null +++ b/csharp/08-stack/algo08_stack/algo08_stack.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp2.2 + + + diff --git a/csharp/Tests/_05_array_tests/Array.Tests.cs b/csharp/Tests/_05_array_tests/Array.Tests.cs new file mode 100644 index 00000000..5e4f88f6 --- /dev/null +++ b/csharp/Tests/_05_array_tests/Array.Tests.cs @@ -0,0 +1,231 @@ +using System; +using algo05_array; +using Xunit; +using Xunit.Abstractions; + +namespace _05_array_tests +{ + public class ArrayTests + { + private readonly Array _sqList; + private readonly ITestOutputHelper _output; + + public ArrayTests(ITestOutputHelper output) + { + _sqList = new Array(10); + _output = output; + } + + private void PrintList() + { + for (int idx = 0; idx < _sqList.Length; idx++) + { + var elem = _sqList.Find(idx); + _output.WriteLine(elem.ToString()); + } + } + + [Fact] + public void Length_Equal_1_After_InsertOneElement() + { + _sqList.Insert(0, 1); + Assert.True(_sqList.Length == 1); + } + + [Fact] + public void Insert_ThrowIndexOutOfRangeException_When_Index_GreaterThan_Length() + { + _sqList.Insert(0, 1); + Exception ex = Assert.Throws(() => _sqList.Insert(3, 2)); + Assert.IsType(ex); + } + + [Fact] + public void Insert_ThrowIndexOutOfRangeException_When_Index_LessThan_Zero() + { + Exception ex = Assert.Throws(() => _sqList.Insert(-1, 1)); + Assert.IsType(ex); + } + + [Fact] + public void Insert_ThrowIndexOutOfRangeException_When_List_Is_Full() + { + _sqList.Insert(0, 11); + _sqList.Insert(1, 10); + _sqList.Insert(2, 9); + _sqList.Insert(3, 8); + _sqList.Insert(4, 7); + _sqList.Insert(5, 6); + _sqList.Insert(6, 5); + _sqList.Insert(7, 4); + _sqList.Insert(8, 3); + _sqList.Insert(9, 2); + + PrintList(); + + Exception ex = Assert.Throws(() => _sqList.Insert(10, 101)); + Assert.IsType(ex); + } + + [Fact] + public void Delete_ThrowIndexOutOfRangeException_When_Index_LessThan_Zero() + { + Exception ex = Assert.Throws(() => _sqList.Delete(-1)); + Assert.IsType(ex); + } + + [Fact] + public void Delete_ThrowIndexOutOfRangeException_When_Index_GreaterThan_Length() + { + _sqList.Insert(0, 11); + _sqList.Insert(1, 22); + Exception ex = Assert.Throws(() => _sqList.Delete(3)); + Assert.IsType(ex); + } + + [Fact] + public void Delete_First_Element_Success() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + + bool ret = _sqList.Delete(1); + Assert.True(ret); + } + + [Fact] + public void Delete_Last_Element_Success() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + bool ret = _sqList.Delete(3); + Assert.True(ret); + } + + [Fact] + public void Delete_Middle_Element() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + bool ret = _sqList.Delete(2); + Assert.True(ret); + } + + [Fact] + public void Find_ThrowsIndexOutOfRangeException_When_Index_LessThan_Zero() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + Exception ex = Assert.Throws(() => _sqList.Find(-1)); + Assert.IsType(ex); + } + + [Fact] + public void Find_ThrowsIndexOutOfRangeException_When_Index_GreaterThan_Length() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + Exception ex = Assert.Throws(() => _sqList.Find(4)); + Assert.IsType(ex); + } + + [Fact] + public void Find_Last_Position_Return_33() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + var elem = _sqList.Find(3); + + Assert.Equal(33, elem); + } + + [Fact] + public void Find_First_Element_Return_11() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + var elem = _sqList.Find(1); + + Assert.Equal(11, elem); + } + + [Fact] + public void IndexOf_Return_Negative_1_When_Element_Not_Exist() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + var elem = _sqList.IndexOf(55); + + Assert.Equal(-1, elem); + } + + [Fact] + public void IndexOf_Return_First_Index() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + var elem = _sqList.IndexOf(100); + + Assert.Equal(0, elem); + } + + [Fact] + public void IndexOf_Return_Last_Index() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 11); + _sqList.Insert(2, 22); + _sqList.Insert(3, 33); + + var elem = _sqList.IndexOf(33); + + Assert.Equal(3, elem); + } + + [Fact] + public void Clear_Then_Length_Equal_Zero() + { + _sqList.Insert(0, 100); + _sqList.Insert(1, 10); + _sqList.Insert(2, 9); + _sqList.Insert(3, 8); + _sqList.Insert(4, 7); + _sqList.Insert(5, 6); + _sqList.Insert(6, 5); + _sqList.Insert(7, 4); + _sqList.Insert(8, 3); + _sqList.Insert(9, 2); + + Assert.Equal(10, _sqList.Length); + + _sqList.Clear(); + + Assert.Equal(0, _sqList.Length); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/_05_array_tests/algo05_array_tests.csproj b/csharp/Tests/_05_array_tests/algo05_array_tests.csproj new file mode 100644 index 00000000..2e90daaf --- /dev/null +++ b/csharp/Tests/_05_array_tests/algo05_array_tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.2 + + false + + 05_array_tests + + + + + + + + + + + + + diff --git a/csharp/Tests/_06_linkedlist_tests/BaseLinkedListTests.cs b/csharp/Tests/_06_linkedlist_tests/BaseLinkedListTests.cs new file mode 100644 index 00000000..2a6a72db --- /dev/null +++ b/csharp/Tests/_06_linkedlist_tests/BaseLinkedListTests.cs @@ -0,0 +1,20 @@ +using System; +using algo06_linked_list; + +namespace algo06_linkedlist_tests +{ + public class BaseLinkedListTests + { + protected void PrintLinkedList (SingleLinkedList list) where T : IComparable + { + if (list == null) return; + + var p = list.First; + while (p != null) + { + System.Console.WriteLine (p.Value); + p = p.Next; + } + } + } +} \ No newline at end of file diff --git a/csharp/Tests/_06_linkedlist_tests/LRUWithArray.Tests.cs b/csharp/Tests/_06_linkedlist_tests/LRUWithArray.Tests.cs new file mode 100644 index 00000000..88fcec5c --- /dev/null +++ b/csharp/Tests/_06_linkedlist_tests/LRUWithArray.Tests.cs @@ -0,0 +1,82 @@ +using algo05_array; +using algo06_linked_list; +using Xunit; +using Xunit.Abstractions; + +namespace algo06_linkedlist_tests +{ + public class LRUWithArrayTests + { + private ITestOutputHelper _output; + + public LRUWithArrayTests(ITestOutputHelper output) + { + _output = output; + } + + private void PrintList(Array list) + { + if (list == null) return; + for (int idx = 0; idx < list.Length; idx++) + { + _output.WriteLine(list.Find(idx).ToString()); + } + } + + [Fact] + public void LRU_Set_Value_When_Not_Existed() + { + var lru = new LRUWithArray(5); + + lru.Set(1); + lru.Set(3); + lru.Set(5); + lru.Set(7); + lru.Set(9); + + var list = lru.CachedList; + + PrintList(list); + + Assert.Equal(9, list.Find(0)); + } + + + [Fact] + public void LRU_Set_Value_When_Existed() + { + var lru = new LRUWithArray(5); + + lru.Set(1); + lru.Set(3); + lru.Set(5); + lru.Set(7); + lru.Set(3); + + var list = lru.CachedList; + + PrintList(list); + + Assert.Equal(3, list.Find(0)); + } + + [Fact] + public void LRU_Set_Value_When_Full() + { + var lru = new LRUWithArray(5); + + lru.Set(1); + lru.Set(3); + lru.Set(5); + lru.Set(7); + lru.Set(9); + lru.Set(8); + + var list = lru.CachedList; + + PrintList(list); + + Assert.Equal(8, list.Find(0)); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/_06_linkedlist_tests/LRUWithLinkedList.Tests.cs b/csharp/Tests/_06_linkedlist_tests/LRUWithLinkedList.Tests.cs new file mode 100644 index 00000000..3136d92c --- /dev/null +++ b/csharp/Tests/_06_linkedlist_tests/LRUWithLinkedList.Tests.cs @@ -0,0 +1,64 @@ +using Xunit; +using Xunit.Abstractions; +using algo06_linked_list; + +namespace algo06_linkedlist_tests +{ + public class LRUWithLinkedListTests : BaseLinkedListTests + { + [Fact] + public void LRU_Set_Value_When_Not_Existed() + { + var lru = new LRUWithLinkedList(); + + lru.Set(1); + lru.Set(3); + lru.Set(5); + lru.Set(7); + lru.Set(9); + + var list = lru.CachedList; + + PrintLinkedList(list); + + Assert.Equal(9, list.First.Value); + } + + [Fact] + public void LRU_Set_Value_When_Existed() + { + var lru = new LRUWithLinkedList(); + + lru.Set(1); + lru.Set(3); + lru.Set(5); + lru.Set(7); + lru.Set(3); + + var list = lru.CachedList; + + PrintLinkedList(list); + + Assert.Equal(3, list.First.Value); + } + + [Fact] + public void LRU_Set_Value_When_Full() + { + var lru = new LRUWithLinkedList(5); + + lru.Set(1); + lru.Set(3); + lru.Set(5); + lru.Set(7); + lru.Set(9); + lru.Set(8); + + var list = lru.CachedList; + + PrintLinkedList(list); + + Assert.Equal(8, list.First.Value); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/_06_linkedlist_tests/SingleLinkedList.Tests.cs b/csharp/Tests/_06_linkedlist_tests/SingleLinkedList.Tests.cs new file mode 100644 index 00000000..8f577b71 --- /dev/null +++ b/csharp/Tests/_06_linkedlist_tests/SingleLinkedList.Tests.cs @@ -0,0 +1,172 @@ +using System; +using Xunit; +using Xunit.Abstractions; +using algo06_linked_list; + +namespace algo06_linkedlist_tests +{ + public class SingleLinkedListTests : BaseLinkedListTests + { + [Fact] + public void Insert_3_Elements_Return_Length_3() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + PrintLinkedList(list); + + Assert.Equal(3, list.Length); + } + + [Fact] + public void Insert_Some_Elements_Then_Verify_First() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + Assert.Equal("The", list.First.Value); + } + + [Fact] + public void Insert_Some_Elements_Then_Verify_Last() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + Assert.Equal("Brown", list.First.Next.Next.Value); + } + + [Fact] + public void Find_Return_Null_When_Position_LessThan_1() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + var node = list.Find(0); + Assert.Null(node); + } + + [Fact] + public void Find_Return_Null_When_Position_GreaterThan_Length() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + var node = list.Find(4); + Assert.Null(node); + } + + [Fact] + public void Find_Return_Correct_When_Position_Valid() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + var node = list.Find(2); + Assert.Equal("Quick", node.Value); + } + + [Fact] + public void Delete_Return_Null_When_Position_LessThan_1() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + var node = list.Delete(0); + Assert.Null(node); + } + + [Fact] + public void Delete_Return_Null_When_Position_GreaterThan_Length() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + var node = list.Delete(4); + Assert.Null(node); + } + + [Fact] + public void Delete_By_Value_Success_When_Element_Exist() + { + var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + var deletedNode = list.Delete("over"); + + PrintLinkedList(list); + + Assert.Equal("over", deletedNode.Value); + Assert.Equal(8, list.Length); + } + + [Fact] + public void Delete_By_Value_Success_When_Element_Not_Exist() + { + var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + var deletedNode = list.Delete("hello"); + + PrintLinkedList(list); + + Assert.Null(deletedNode); + Assert.Equal(9, list.Length); + } + + [Fact] + public void Delete_By_Value_Success_When_Delete_First() + { + var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + var deletedNode = list.Delete("The"); + + PrintLinkedList(list); + + Assert.Equal("The", deletedNode.Value); + Assert.Equal(8, list.Length); + } + + [Fact] + public void Delete_By_Value_Success_When_Delete_Last() + { + var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + var deletedNode = list.Delete("dog"); + + PrintLinkedList(list); + + Assert.Equal("dog", deletedNode.Value); + Assert.Equal(8, list.Length); + } + + [Fact] + public void Delete_Success_When_Position_Valid() + { + var list = new SingleLinkedList("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + var node = list.Delete(3); + + PrintLinkedList(list); + + Assert.Equal("Brown", node.Value); + Assert.Equal(8, list.Length); + } + + [Fact] + public void Clear_Length_Equal_0() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + list.Clear(); + + Assert.Equal(0, list.Length); + } + + [Fact] + public void Clear_First_Is_Null() + { + var list = new SingleLinkedList("The", "Quick", "Brown"); + + list.Clear(); + + Assert.Null(list.First); + } + + } +} \ No newline at end of file diff --git a/csharp/Tests/_06_linkedlist_tests/algo06_linkedlist_tests.csproj b/csharp/Tests/_06_linkedlist_tests/algo06_linkedlist_tests.csproj new file mode 100644 index 00000000..fad38254 --- /dev/null +++ b/csharp/Tests/_06_linkedlist_tests/algo06_linkedlist_tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + diff --git a/csharp/Tests/_07_linkedlist_tests/SingleLinkedListAlgo.Tests.cs b/csharp/Tests/_07_linkedlist_tests/SingleLinkedListAlgo.Tests.cs new file mode 100644 index 00000000..9527f6aa --- /dev/null +++ b/csharp/Tests/_07_linkedlist_tests/SingleLinkedListAlgo.Tests.cs @@ -0,0 +1,150 @@ +using System; +using Xunit; +using algo06_linkedlist_tests; +using algo06_linked_list; +using algo07_linkedlist; + +namespace algo07_linkedlist_tests +{ + public class SingleLinkedListAlgoTests : BaseLinkedListTests + { + [Fact] + public void Reverse_When_List_Is_Empty() + { + var list = new SingleLinkedListAlgo(); + + list.Reverse(); + + PrintLinkedList(list); + + Assert.Null(list.First); + } + + [Fact] + public void Reverse_When_List_Has_Many_Elements() + { + var list = new SingleLinkedListAlgo("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + list.Reverse(); + + PrintLinkedList(list); + + Assert.True(list.First.Value == "dog"); + } + + [Fact] + public void HasCycle_List_Empty() + { + var list = new SingleLinkedListAlgo("The", "Quick", "Brown", "Fox", "jumps", "over", "the", "lazy", + "dog"); + + bool hasCycle = list.HasCycle(); + + Assert.False(hasCycle); + } + + [Fact] + public void HasCycle_False_When_List_Length_1() + { + var list = new SingleLinkedListAlgo("The"); + + bool hasCycle = list.HasCycle(); + + Assert.False(hasCycle); + } + + [Fact] + public void HasCycle_False_When_List_Length_2() + { + var list = new SingleLinkedListAlgo("The", "Quick"); + + bool hasCycle = list.HasCycle(); + + Assert.False(hasCycle); + } + + [Fact] + public void HasCycle_True_When_List_Length_2() + { + var list = new SingleLinkedListAlgo(); + + var firstNode = list.Insert(1, "The"); + var secondNode = list.Insert(2, "Quick"); + + secondNode.Next = firstNode; + + bool hasCycle = list.HasCycle(); + + Assert.True(hasCycle); + } + + [Fact] + public void HasCycle_False() + { + var linkList = + new SingleLinkedListAlgo("The", "Quick", "Brown", "fox", "jumps", "over", "the", "lazy", "dog"); + + bool hasCycle = linkList.HasCycle(); + + Assert.False(hasCycle); + } + + [Fact] + public void HasCycle_True() + { + var list = new SingleLinkedListAlgo(); + + // 初始化一个具有环的链表 + list.Insert(1, "The"); + list.Insert(2, "Quick"); + list.Insert(3, "Brown"); + var fourthNode = list.Insert(4, "fox"); + list.Insert(5, "jumps"); + list.Insert(6, "over"); + list.Insert(7, "the"); + list.Insert(8, "lazy"); + var last = list.Insert(9, "dog"); + + last.Next = fourthNode; + + bool hasCycle = list.HasCycle(); + + Assert.True(hasCycle); + } + + [Fact] + public void Merge() + { + var list1 = new SingleLinkedListAlgo(1, 2, 4); + var list2 = new SingleLinkedListAlgo(1, 3, 4); + + var list3 = list1.Merge(list2); + + PrintLinkedList(list3); + + Assert.True(list1.First.Next.Next.Value == 2); + } + + [Fact] + public void Remove_2th_Node_From_End() + { + var list = new SingleLinkedListAlgo(1, 2, 3, 4, 5); + list.RemoveNthNodeFromEnd(2); + + PrintLinkedList(list); + + Assert.True(list.First.Next.Next.Next.Value == 5); + } + + [Fact] + public void FindMiddleNode() + { + var list = new SingleLinkedListAlgo(1, 2, 3, 4, 5); + + ListNode middleNode = list.FindMiddleNode(); + + Assert.True(middleNode.Value == 3); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/_07_linkedlist_tests/algo07_linkedlist_tests.csproj b/csharp/Tests/_07_linkedlist_tests/algo07_linkedlist_tests.csproj new file mode 100644 index 00000000..2e558877 --- /dev/null +++ b/csharp/Tests/_07_linkedlist_tests/algo07_linkedlist_tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + diff --git a/csharp/Tests/algo08_stack_tests/ArrayStack.Tests.cs b/csharp/Tests/algo08_stack_tests/ArrayStack.Tests.cs new file mode 100644 index 00000000..ec28a54b --- /dev/null +++ b/csharp/Tests/algo08_stack_tests/ArrayStack.Tests.cs @@ -0,0 +1,81 @@ +using System; +using algo08_stack; +using Xunit; +using Xunit.Abstractions; + +namespace algo08_stack_tests +{ + public class ArrayStackTests + { + private readonly ITestOutputHelper _output; + + public ArrayStackTests(ITestOutputHelper output) + { + _output = output; + } + + private void PrintStackArray(ArrayStack list) + { + if (list.Count == 0) return; + + while (list.Count > 0) + { + T item = list.Pop(); + _output.WriteLine(item.ToString()); + } + } + + [Fact] + public void Push_3_Elements_Then_Length_Equal_3() + { + var stack = new ArrayStack(5); + stack.Push(2); + stack.Push(4); + stack.Push(6); + + Assert.Equal(3, stack.Count); + + PrintStackArray(stack); + } + + [Fact] + public void Push_Throw_InvalidOperationException_When_Stack_Full() + { + var stack = new ArrayStack(5); + stack.Push(2); + stack.Push(4); + stack.Push(6); + stack.Push(8); + stack.Push(10); + + Exception ex = Assert.Throws(() => stack.Push(11)); + Assert.IsType(ex); + + PrintStackArray(stack); + } + + [Fact] + public void Pop_Throw_InvalidOperationException_When_Stack_Empty() + { + var stack = new ArrayStack(5); + + Exception ex = Assert.Throws(() => stack.Pop()); + Assert.IsType(ex); + + PrintStackArray(stack); + } + + [Fact] + public void Pop_Valid_When_Stack_Not_Empty() + { + var stack = new ArrayStack(5); + stack.Push(2); + stack.Push(4); + + int val = stack.Pop(); + Assert.Equal(4, val); + + PrintStackArray(stack); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/algo08_stack_tests/LinkedStack.Tests.cs b/csharp/Tests/algo08_stack_tests/LinkedStack.Tests.cs new file mode 100644 index 00000000..81916928 --- /dev/null +++ b/csharp/Tests/algo08_stack_tests/LinkedStack.Tests.cs @@ -0,0 +1,65 @@ +using System; +using algo08_stack; +using Xunit; +using Xunit.Abstractions; + +namespace algo08_stack_tests +{ + public class LinkedStackTests + { + private readonly ITestOutputHelper _output; + + public LinkedStackTests(ITestOutputHelper output) + { + _output = output; + } + + private void PrintStackLinkedList(LinkedStack list) + { + if (list.Count == 0) return; + + while (list.Count > 0) + { + var val = list.Pop(); + _output.WriteLine(val.ToString()); + } + } + + [Fact] + public void Push_3_Elements_Then_Length_Equal_3() + { + var stack = new LinkedStack(); + stack.Push(2); + stack.Push(4); + stack.Push(6); + + Assert.Equal(3, stack.Count); + + PrintStackLinkedList(stack); + } + + [Fact] + public void Pop_Throw_InvalidOperationException_When_Stack_Empty() + { + var stack = new LinkedStack(); + + Exception ex = Assert.Throws(() => stack.Pop()); + Assert.IsType(ex); + + PrintStackLinkedList(stack); + } + + [Fact] + public void Pop_Valid_When_Stack_Not_Empty() + { + var stack = new LinkedStack(); + stack.Push(2); + stack.Push(4); + + var nodeVal = stack.Pop(); + Assert.Equal(4, nodeVal); + + PrintStackLinkedList(stack); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/algo08_stack_tests/LinkedStackBrowser.Tests.cs b/csharp/Tests/algo08_stack_tests/LinkedStackBrowser.Tests.cs new file mode 100644 index 00000000..5d0a1139 --- /dev/null +++ b/csharp/Tests/algo08_stack_tests/LinkedStackBrowser.Tests.cs @@ -0,0 +1,107 @@ +using algo08_stack; +using Xunit; +using Xunit.Abstractions; + +namespace algo08_stack_tests +{ + public class LinkedStackBrowserTests + { + private readonly ITestOutputHelper _output; + + public LinkedStackBrowserTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Browser_Open_4_Links_Back_2_Return_Right_Link() + { + var browser = new LinkedStackBrowser(); + browser.Open("www.google.com"); + browser.Open("www.baidu.com"); + browser.Open("www.qq.com"); + browser.Open("www.dadu.com"); + + string url = browser.Backward(); + url = browser.Backward(); + + Assert.Equal("www.qq.com", url); + } + + [Fact] + public void Browser_Open_4_Links_Back_4_Return_Empty() + { + var browser = new LinkedStackBrowser(); + browser.Open("www.google.com"); + browser.Open("www.baidu.com"); + browser.Open("www.qq.com"); + browser.Open("www.dadu.com"); + + browser.Backward(); + browser.Backward(); + browser.Backward(); + browser.Backward(); + string url = browser.Backward(); + + Assert.Equal(string.Empty, url); + } + + [Fact] + public void Browser_Forward_Before_End() + { + var browser = new LinkedStackBrowser(); + browser.Open("www.google.com"); + browser.Open("www.baidu.com"); + browser.Open("www.qq.com"); + browser.Open("www.dadu.com"); + + browser.Backward(); + browser.Backward(); + browser.Backward(); + + browser.Forward(); + string url = browser.Forward(); + + Assert.Equal("www.qq.com", url); + } + + [Fact] + public void Browser_Forward_Until_End() + { + var browser = new LinkedStackBrowser(); + browser.Open("www.google.com"); + browser.Open("www.baidu.com"); + browser.Open("www.qq.com"); + browser.Open("www.dadu.com"); + + browser.Backward(); + browser.Backward(); + browser.Backward(); + + browser.Forward(); + browser.Forward(); + browser.Forward(); + string url = browser.Forward(); + + Assert.Equal(string.Empty, url); + } + + [Fact] + public void Browser_Backward_And_Open_New_Then_Cannot_Forward() + { + var browser = new LinkedStackBrowser(); + browser.Open("www.google.com"); + browser.Open("www.baidu.com"); + browser.Open("www.qq.com"); + + browser.Backward(); + browser.Backward(); + + browser.Open("www.dadu.com"); + + string url = browser.Forward(); + + Assert.Equal(string.Empty, url); + } + } +} \ No newline at end of file diff --git a/csharp/Tests/algo08_stack_tests/algo08_stack_tests.csproj b/csharp/Tests/algo08_stack_tests/algo08_stack_tests.csproj new file mode 100644 index 00000000..0c4d1a46 --- /dev/null +++ b/csharp/Tests/algo08_stack_tests/algo08_stack_tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/csharp/csharp.sln b/csharp/csharp.sln new file mode 100644 index 00000000..1f4b949b --- /dev/null +++ b/csharp/csharp.sln @@ -0,0 +1,140 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo05_array", "05-array\algo05_array.csproj", "{B88033F6-FF08-434A-AED7-91F5CDB73402}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo06_linked_list", "06-linkedlist\algo06_linked_list.csproj", "{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo05_array_tests", "Tests\_05_array_tests\algo05_array_tests.csproj", "{14982212-49E4-4409-8BFD-2D8A2945BD83}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo06_linkedlist_tests", "Tests\_06_linkedlist_tests\algo06_linkedlist_tests.csproj", "{1B93D9C6-D6C1-4619-BFB9-D84C29099223}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo07_linkedlist", "07-linkedlist\_07_linkedlist\algo07_linkedlist.csproj", "{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo07_linkedlist_tests", "Tests\_07_linkedlist_tests\algo07_linkedlist_tests.csproj", "{66C3BC00-C135-4279-A666-A330A86F20D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo08_stack", "08-stack\algo08_stack\algo08_stack.csproj", "{E080D481-C98E-43F3-B1D1-51DCF4CF7146}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "algo08_stack_tests", "Tests\algo08_stack_tests\algo08_stack_tests.csproj", "{6A249475-54EA-4039-9B0C-DC0E0A884C07}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x64.ActiveCfg = Debug|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x64.Build.0 = Debug|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x86.ActiveCfg = Debug|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x86.Build.0 = Debug|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|Any CPU.Build.0 = Release|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x64.ActiveCfg = Release|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x64.Build.0 = Release|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x86.ActiveCfg = Release|Any CPU + {B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x86.Build.0 = Release|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x64.ActiveCfg = Debug|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x64.Build.0 = Debug|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x86.ActiveCfg = Debug|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x86.Build.0 = Debug|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|Any CPU.Build.0 = Release|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x64.ActiveCfg = Release|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x64.Build.0 = Release|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x86.ActiveCfg = Release|Any CPU + {29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x86.Build.0 = Release|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x64.ActiveCfg = Debug|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x64.Build.0 = Debug|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x86.ActiveCfg = Debug|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x86.Build.0 = Debug|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|Any CPU.Build.0 = Release|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x64.ActiveCfg = Release|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x64.Build.0 = Release|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x86.ActiveCfg = Release|Any CPU + {14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x86.Build.0 = Release|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x64.ActiveCfg = Debug|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x64.Build.0 = Debug|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x86.ActiveCfg = Debug|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x86.Build.0 = Debug|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|Any CPU.Build.0 = Release|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x64.ActiveCfg = Release|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x64.Build.0 = Release|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x86.ActiveCfg = Release|Any CPU + {1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x86.Build.0 = Release|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x64.Build.0 = Debug|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x86.Build.0 = Debug|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|Any CPU.Build.0 = Release|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x64.ActiveCfg = Release|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x64.Build.0 = Release|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x86.ActiveCfg = Release|Any CPU + {5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x86.Build.0 = Release|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x64.ActiveCfg = Debug|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x64.Build.0 = Debug|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x86.ActiveCfg = Debug|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x86.Build.0 = Debug|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|Any CPU.Build.0 = Release|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x64.ActiveCfg = Release|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x64.Build.0 = Release|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x86.ActiveCfg = Release|Any CPU + {66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x86.Build.0 = Release|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x64.ActiveCfg = Debug|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x64.Build.0 = Debug|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x86.ActiveCfg = Debug|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x86.Build.0 = Debug|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|Any CPU.Build.0 = Release|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x64.ActiveCfg = Release|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x64.Build.0 = Release|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x86.ActiveCfg = Release|Any CPU + {E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x86.Build.0 = Release|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x64.ActiveCfg = Debug|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x64.Build.0 = Debug|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x86.ActiveCfg = Debug|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x86.Build.0 = Debug|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|Any CPU.Build.0 = Release|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x64.ActiveCfg = Release|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x64.Build.0 = Release|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x86.ActiveCfg = Release|Any CPU + {6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {14982212-49E4-4409-8BFD-2D8A2945BD83} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} + {1B93D9C6-D6C1-4619-BFB9-D84C29099223} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} + {66C3BC00-C135-4279-A666-A330A86F20D5} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} + {6A249475-54EA-4039-9B0C-DC0E0A884C07} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB} + EndGlobalSection +EndGlobal diff --git a/csharp/csharp.sln.DotSettings.user b/csharp/csharp.sln.DotSettings.user new file mode 100644 index 00000000..3e96df59 --- /dev/null +++ b/csharp/csharp.sln.DotSettings.user @@ -0,0 +1,10 @@ + + True + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True \ No newline at end of file diff --git a/f21 b/f21 new file mode 100644 index 00000000..72ef739c --- /dev/null +++ b/f21 @@ -0,0 +1,63 @@ +// A Stack based C++ program to find next +// greater element for all array elements +// in same order as input. +#include + +using namespace std; + +/* prints element and NGE pair for all +elements of arr[] of size n */ +void printNGE(int arr[], int n) +{ + stack s; + unordered_map mp; + + /* push the first element to stack */ + s.push(arr[0]); + + + // iterate for rest of the elements + for (int i = 1; i < n; i++) { + + if (s.empty()) { + s.push(arr[i]); + continue; + } + + /* if stack is not empty, then + pop an element from stack. + If the popped element is smaller + than next, then + a) print the pair + b) keep popping while elements are + smaller and stack is not empty */ + while (s.empty() == false && s.top() < arr[i]) { + mp[s.top()] = arr[i]; + s.pop(); + } + + /* push next to stack so that we can find + next smaller for it */ + s.push(arr[i]); + } + + /* After iterating over the loop, the remaining +elements in stack do not have the next smaller +element, so print -1 for them */ + while (s.empty() == false) { + mp[s.top()] = -1; + s.pop(); + } + + for (int i=0; i " << mp[arr[i]] << endl; +} + +/* Driver program to test above functions */ +int main() +{ + int arr[] = { 11, 13, 21, 3 }; + int n = sizeof(arr) / sizeof(arr[0]); + printNGE(arr, n); + return 0; +} diff --git a/go/05_array/array.go b/go/05_array/array.go index ce2e05a4..c1a861d3 100644 --- a/go/05_array/array.go +++ b/go/05_array/array.go @@ -34,7 +34,7 @@ func (this *Array) Len() uint { //判断索引是否越界 func (this *Array) isIndexOutOfRange(index uint) bool { - if index >= this.length { + if index >= uint(cap(this.data)) { return true } return false diff --git a/go/06_linkedlist/palindrome-linked-list.go b/go/06_linkedlist/palindrome-linked-list.go new file mode 100644 index 00000000..f9f2e4a5 --- /dev/null +++ b/go/06_linkedlist/palindrome-linked-list.go @@ -0,0 +1,41 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func isPalindrome(head *ListNode) bool { + var slow *ListNode = head + var fast *ListNode = head + var prev *ListNode = nil + var temp *ListNode = nil + + if (head == nil || head.Next == nil) { + return true + } + + for (fast != nil && fast.Next !=nil){ + fast = fast.Next.Next + temp = slow.Next + slow.Next = prev + prev = slow + slow = temp + } // 快的先跑完,同时反转了一半链表,剪短 + + if fast != nil { + slow = slow.Next // 处理余数,跨过中位数 + // prev 增加中 2->1->nil + } + + var l1 *ListNode = prev + var l2 *ListNode = slow + + for (l1 != nil && l2 !=nil && l1.Val == l2.Val){ + l1 = l1.Next + l2 = l2.Next + } + + return (l1 == nil && l2 == nil) + + } diff --git a/go/08_stack/StackBasedOnArray.go b/go/08_stack/StackBasedOnArray.go index cdecbf8b..27e3bec8 100644 --- a/go/08_stack/StackBasedOnArray.go +++ b/go/08_stack/StackBasedOnArray.go @@ -28,12 +28,17 @@ func (this *ArrayStack) IsEmpty() bool { } func (this *ArrayStack) Push(v interface{}) { - this.data = append(this.data, v) if this.top < 0 { this.top = 0 } else { this.top += 1 } + + if this.top > len(this.data)-1 { + this.data = append(this.data, v) + } else { + this.data[this.top] = v + } } func (this *ArrayStack) Pop() interface{} { diff --git a/go/08_stack/StackBasedOnArray_test.go b/go/08_stack/StackBasedOnArray_test.go index 0a6ea7dd..55dc8003 100644 --- a/go/08_stack/StackBasedOnArray_test.go +++ b/go/08_stack/StackBasedOnArray_test.go @@ -6,7 +6,12 @@ func TestArrayStack_Push(t *testing.T) { s := NewArrayStack() s.Push(1) s.Push(2) + t.Log(s.Pop()) s.Push(3) + t.Log(s.Pop()) + t.Log(s.Pop()) + s.Push(4) + t.Log(s.Pop()) s.Print() } diff --git a/go/09_queue/CircularQueue.go b/go/09_queue/CircularQueue.go new file mode 100644 index 00000000..c2284909 --- /dev/null +++ b/go/09_queue/CircularQueue.go @@ -0,0 +1,72 @@ +package _9_queue + +import "fmt" + +type CircularQueue struct { + q []interface{} + capacity int + head int + tail int +} + +func NewCircularQueue(n int) *CircularQueue { + if n == 0 { + return nil + } + return &CircularQueue{make([]interface{}, n), n, 0, 0} +} + +/* +栈空条件:head==tail为true +*/ +func (this *CircularQueue) IsEmpty() bool { + if this.head == this.tail { + return true + } + return false +} + +/* +栈满条件:(tail+1)%capacity==head为true +*/ +func (this *CircularQueue) IsFull() bool { + if this.head == (this.tail+1)%this.capacity { + return true + } + return false +} + +func (this *CircularQueue) EnQueue(v interface{}) bool { + if this.IsFull() { + return false + } + this.q[this.tail] = v + this.tail = (this.tail + 1) % this.capacity + return true +} + +func (this *CircularQueue) DeQueue() interface{} { + if this.IsEmpty() { + return nil + } + v := this.q[this.head] + this.head = (this.head + 1) % this.capacity + return v +} + +func (this *CircularQueue) String() string { + if this.IsEmpty() { + return "empty queue" + } + result := "head" + var i = this.head + for true { + result += fmt.Sprintf("<-%+v", this.q[i]) + i = (i + 1) % this.capacity + if i == this.tail { + break + } + } + result += "<-tail" + return result +} diff --git a/go/09_queue/CircularQueue_test.go b/go/09_queue/CircularQueue_test.go new file mode 100644 index 00000000..59c64417 --- /dev/null +++ b/go/09_queue/CircularQueue_test.go @@ -0,0 +1,37 @@ +package _9_queue + +import "testing" + +func TestCircularQueue_EnQueue(t *testing.T) { + q := NewCircularQueue(5) + q.EnQueue(1) + q.EnQueue(2) + q.EnQueue(3) + q.EnQueue(4) + q.EnQueue(5) + q.EnQueue(6) + t.Log(q) +} + +func TestCircularQueue_DeQueue(t *testing.T) { + q := NewCircularQueue(5) + q.EnQueue(1) + q.EnQueue(2) + q.EnQueue(3) + q.EnQueue(4) + q.EnQueue(5) + q.EnQueue(6) + t.Log(q) + t.Log(q.DeQueue()) + t.Log(q) + q.EnQueue(5) + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) +} diff --git a/go/09_queue/QueueBasedOnArray.go b/go/09_queue/QueueBasedOnArray.go new file mode 100644 index 00000000..d70335f5 --- /dev/null +++ b/go/09_queue/QueueBasedOnArray.go @@ -0,0 +1,44 @@ +package _9_queue + +import "fmt" + +type ArrayQueue struct { + q []interface{} + capacity int + head int + tail int +} + +func NewArrayQueue(n int) *ArrayQueue { + return &ArrayQueue{make([]interface{}, n), n, 0, 0} +} + +func (this *ArrayQueue) EnQueue(v interface{}) bool { + if this.tail == this.capacity { + return false + } + this.q[this.tail] = v + this.tail++ + return true +} + +func (this *ArrayQueue) DeQueue() interface{} { + if this.head == this.tail { + return nil + } + v := this.q[this.head] + this.head++ + return v +} + +func (this *ArrayQueue) String() string { + if this.head == this.tail { + return "empty queue" + } + result := "head" + for i := this.head; i <= this.tail-1; i++ { + result += fmt.Sprintf("<-%+v", this.q[i]) + } + result += "<-tail" + return result +} diff --git a/go/09_queue/QueueBasedOnArray_test.go b/go/09_queue/QueueBasedOnArray_test.go new file mode 100644 index 00000000..50335f7f --- /dev/null +++ b/go/09_queue/QueueBasedOnArray_test.go @@ -0,0 +1,35 @@ +package _9_queue + +import "testing" + +func TestArrayQueue_EnQueue(t *testing.T) { + q := NewArrayQueue(5) + q.EnQueue(1) + q.EnQueue(2) + q.EnQueue(3) + q.EnQueue(4) + q.EnQueue(5) + q.EnQueue(6) + t.Log(q) +} + +func TestArrayQueue_DeQueue(t *testing.T) { + q := NewArrayQueue(5) + q.EnQueue(1) + q.EnQueue(2) + q.EnQueue(3) + q.EnQueue(4) + q.EnQueue(5) + q.EnQueue(6) + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) +} diff --git a/go/09_queue/QueueBasedOnLinkedList.go b/go/09_queue/QueueBasedOnLinkedList.go new file mode 100644 index 00000000..65d898da --- /dev/null +++ b/go/09_queue/QueueBasedOnLinkedList.go @@ -0,0 +1,52 @@ +package _9_queue + +import "fmt" + +type ListNode struct { + val interface{} + next *ListNode +} + +type LinkedListQueue struct { + head *ListNode + tail *ListNode + length int +} + +func NewLinkedListQueue() *LinkedListQueue { + return &LinkedListQueue{nil, nil, 0} +} + +func (this *LinkedListQueue) EnQueue(v interface{}) { + node := &ListNode{v, nil} + if nil == this.tail { + this.tail = node + this.head = node + } else { + this.tail.next = node + this.tail = node + } + this.length++ +} + +func (this *LinkedListQueue) DeQueue() interface{} { + if this.head == nil { + return nil + } + v := this.head.val + this.head = this.head.next + this.length-- + return v +} + +func (this *LinkedListQueue) String() string { + if this.head == nil { + return "empty queue" + } + result := "head<-" + for cur := this.head; cur != nil; cur = cur.next { + result += fmt.Sprintf("<-%+v", cur.val) + } + result += "<-tail" + return result +} diff --git a/go/09_queue/QueueBasedOnLinkedList_test.go b/go/09_queue/QueueBasedOnLinkedList_test.go new file mode 100644 index 00000000..425c6e8a --- /dev/null +++ b/go/09_queue/QueueBasedOnLinkedList_test.go @@ -0,0 +1,35 @@ +package _9_queue + +import "testing" + +func TestListQueue_EnQueue(t *testing.T) { + q := NewLinkedListQueue() + q.EnQueue(1) + q.EnQueue(2) + q.EnQueue(3) + q.EnQueue(4) + q.EnQueue(5) + q.EnQueue(6) + t.Log(q) +} + +func TestListQueue_DeQueue(t *testing.T) { + q := NewLinkedListQueue() + q.EnQueue(1) + q.EnQueue(2) + q.EnQueue(3) + q.EnQueue(4) + q.EnQueue(5) + q.EnQueue(6) + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) + q.DeQueue() + t.Log(q) +} diff --git a/go/10_recursion/Factorial.go b/go/10_recursion/Factorial.go new file mode 100644 index 00000000..282bf431 --- /dev/null +++ b/go/10_recursion/Factorial.go @@ -0,0 +1,31 @@ +package Recursion + +// 迭代实现阶乘 +type Fac struct { + val map[int]int +} + +func NewFactorial(n int) *Fac { + return &Fac{ + make(map[int]int, n), + } +} + +func (fac *Fac) Factorial(n int) int { + if fac.val[n] != 0{ + return fac.val[n] + } + + if n <= 1{ + fac.val[n] = 1 + return 1 + }else { + res := n * fac.Factorial(n-1) + fac.val[n] =res + return res + } +} + +func (fac *Fac) Print(n int ) { + println(fac.val[n]) +} diff --git a/go/10_recursion/Factorial_test.go b/go/10_recursion/Factorial_test.go new file mode 100644 index 00000000..6a90d3ff --- /dev/null +++ b/go/10_recursion/Factorial_test.go @@ -0,0 +1,11 @@ +package Recursion + +import "testing" + +func TestFac_Factorial(t *testing.T) { + fac := NewFactorial(10) + for i:=1; i<15; i++{ + fac.Factorial(i) + fac.Print(i) + } +} diff --git a/go/10_recursion/Fibonacci.go b/go/10_recursion/Fibonacci.go new file mode 100644 index 00000000..33ea885f --- /dev/null +++ b/go/10_recursion/Fibonacci.go @@ -0,0 +1,35 @@ +package Recursion + +import "fmt" + +// 递归实现斐波那契数列 +type Fibs struct { + val map[int]int // 使用字典存储结果 +} + +func NewFibs(n int) *Fibs { + return &Fibs{ + make(map[int]int, n), + } +} + +func (fib *Fibs)Fibonacci(n int) int { + if fib.val[n] != 0{ + return fib.val[n] + } + if n <= 1 { + fib.val[1] = 1 + return 1 + }else if n ==2{ + fib.val[2] = 1 + return 1 + } else { + res := fib.Fibonacci(n-1) + fib.Fibonacci(n-2) + fib.val[n] = res + return res + } +} + +func (fib *Fibs)Print(n int) { + fmt.Println(fib.val[n]) +} diff --git a/go/10_recursion/Fibonacci_test.go b/go/10_recursion/Fibonacci_test.go new file mode 100644 index 00000000..37df9607 --- /dev/null +++ b/go/10_recursion/Fibonacci_test.go @@ -0,0 +1,11 @@ +package Recursion + +import "testing" + +func TestFibs_Fibonacci(t *testing.T) { + fib := NewFibs(10) + for i:=1; i<15; i++{ + fib.Fibonacci(i) + fib.Print(i) + } +} \ No newline at end of file diff --git a/go/10_recursion/RangAll.go b/go/10_recursion/RangAll.go new file mode 100644 index 00000000..0ccbe036 --- /dev/null +++ b/go/10_recursion/RangAll.go @@ -0,0 +1,40 @@ +package Recursion + +import ( + "fmt" +) +// 实现一组数据集合的全排列 +type RangeType struct { + value []interface{} +} + +func NewRangeArray(n int) *RangeType { + return &RangeType{ + make([]interface{},n), + } +} + +func (slice *RangeType)RangeALL( start int) { + len := len(slice.value) + if start == len-1{ + // 如果已经是最后位置,直接将数组数据合并输出 + fmt.Println(slice.value) + } + + for i:=start; i a[j+1] { + a[j], a[j+1] = a[j+1], a[j] + //此次冒泡有数据交换 + flag = true + } + } + // 如果没有交换数据,提前退出 + if !flag { + break + } + } +} + +// 插入排序,a表示数组,n表示数组大小 +func InsertionSort(a []int, n int) { + if n <= 1 { + return + } + for i := 1; i < n; i++ { + value := a[i] + j := i - 1 + //查找要插入的位置并移动数据 + for ; j >= 0; j-- { + if a[j] > value { + a[j+1] = a[j] + } else { + break + } + } + a[j+1] = value + } +} + +// 选择排序,a表示数组,n表示数组大小 +func SelectionSort(a []int, n int) { + if n <= 1 { + return + } + for i := 0; i < n; i++ { + // 查找最小值 + minIndex := i + for j := i + 1; j < n; j++ { + if a[j] < a[minIndex] { + minIndex = j + } + } + // 交换 + a[i], a[minIndex] = a[minIndex],a[i] + + } +} diff --git a/go/11_sorts/Sort_test.go b/go/11_sorts/Sort_test.go new file mode 100644 index 00000000..3f1fbc8e --- /dev/null +++ b/go/11_sorts/Sort_test.go @@ -0,0 +1,27 @@ +package _1_sorts + +import ( + "fmt" + "testing" +) + +func TestBubbleSort(t *testing.T) { + arr := []int{1,5,9,6,3,7,5,10} + fmt.Println("排序前:",arr) + BubbleSort(arr,len(arr)) + fmt.Println("排序后:",arr) +} + +func TestInsertionSort(t *testing.T) { + arr := []int{1,5,9,6,3,7,5,10} + fmt.Println("排序前:",arr) + InsertionSort(arr,len(arr)) + fmt.Println("排序后:",arr) +} + +func TestSelectionSort(t *testing.T) { + arr := []int{1,5,9,6,3,7,5,10} + fmt.Println("排序前:",arr) + SelectionSort(arr,len(arr)) + fmt.Println("排序后:",arr) +} diff --git a/go/12_sorts/MergeSort.go b/go/12_sorts/MergeSort.go new file mode 100644 index 00000000..99babd41 --- /dev/null +++ b/go/12_sorts/MergeSort.go @@ -0,0 +1,48 @@ +package _2_sorts + +func MergeSort(arr []int) { + arrLen := len(arr) + if arrLen <= 1 { + return + } + + mergeSort(arr, 0, arrLen-1) +} + +func mergeSort(arr []int, start, end int) { + if start >= end { + return + } + + mid := (start + end) / 2 + mergeSort(arr, start, mid) + mergeSort(arr, mid+1, end) + merge(arr, start, mid, end) +} + +func merge(arr []int, start, mid, end int) { + tmpArr := make([]int, end-start+1) + + i := start + j := mid + 1 + k := 0 + for ; i <= mid && j <= end; k++ { + if arr[i] < arr[j] { + tmpArr[k] = arr[i] + i++ + } else { + tmpArr[k] = arr[j] + j++ + } + } + + for ; i <= mid; i++ { + tmpArr[k] = arr[i] + k++ + } + for ; j <= end; j++ { + tmpArr[k] = arr[j] + k++ + } + copy(arr[start:end+1], tmpArr) +} diff --git a/go/12_sorts/MergeSort_test.go b/go/12_sorts/MergeSort_test.go new file mode 100644 index 00000000..693bab1f --- /dev/null +++ b/go/12_sorts/MergeSort_test.go @@ -0,0 +1,13 @@ +package _2_sorts + +import "testing" + +func TestMergeSort(t *testing.T) { + arr := []int{5, 4} + MergeSort(arr) + t.Log(arr) + + arr = []int{5, 4, 3, 2, 1} + MergeSort(arr) + t.Log(arr) +} diff --git a/go/12_sorts/QuickSort.go b/go/12_sorts/QuickSort.go new file mode 100644 index 00000000..37d9803d --- /dev/null +++ b/go/12_sorts/QuickSort.go @@ -0,0 +1,35 @@ +package _2_sorts + +// QuickSort is quicksort methods for golang +func QuickSort(arr []int) { + separateSort(arr, 0, len(arr)-1) +} + +func separateSort(arr []int, start, end int) { + if start >= end { + return + } + i := partition(arr, start, end) + separateSort(arr, start, i-1) + separateSort(arr, i+1, end) +} + +func partition(arr []int, start, end int) int { + // 选取最后一位当对比数字 + pivot := arr[end] + + var i = start + for j := start; j < end; j++ { + if arr[j] < pivot { + if !(i == j) { + // 交换位置 + arr[i], arr[j] = arr[j], arr[i] + } + i++ + } + } + + arr[i], arr[end] = arr[end], arr[i] + + return i +} diff --git a/go/12_sorts/QuickSort_test.go b/go/12_sorts/QuickSort_test.go new file mode 100644 index 00000000..569982d6 --- /dev/null +++ b/go/12_sorts/QuickSort_test.go @@ -0,0 +1,24 @@ +package _2_sorts + +import ( + "math/rand" + "testing" +) + +func createRandomArr(length int) []int { + arr := make([]int, length, length) + for i := 0; i < length; i++ { + arr[i] = rand.Intn(100) + } + return arr +} + +func TestQuickSort(t *testing.T) { + arr := []int{5, 4} + QuickSort(arr) + t.Log(arr) + + arr = createRandomArr(100) + QuickSort(arr) + t.Log(arr) +} diff --git a/go/13_sorts/BucketSort.go b/go/13_sorts/BucketSort.go new file mode 100644 index 00000000..13d5d7dc --- /dev/null +++ b/go/13_sorts/BucketSort.go @@ -0,0 +1,69 @@ +package LinearSort + +import ( + "algorithm/Sort" + "fmt" +) + +// 桶排序 + +// 获取待排序数组中的最大值 +func getMax(a []int)int{ + max := a[0] + for i:=1; i max{ + max = a[i] + } + } + return max +} + + +func BucketSort(a []int) { + num := len(a) + if num <= 1{ + return + } + max := getMax(a) + buckets := make([][]int, num) // 二维切片 + + index :=0 + for i:=0; i< num; i++{ + index = a[i]*(num -1) / max // 桶序号 + buckets[index] = append(buckets[index],a[i]) // 加入对应的桶中 + } + + tmpPos := 0 // 标记数组位置 + for i := 0; i < num; i++ { + bucketLen := len(buckets[i]) + if bucketLen > 0{ + Sort.QuickSort(buckets[i]) // 桶内做快速排序 + copy(a[tmpPos:], buckets[i]) + tmpPos += bucketLen + } + } + +} + + +// 桶排序简单实现 +func BucketSortSimple(source []int) { + if len(source)<=1{ + return + } + array := make([]int, getMax(source)+1) + for i:=0; i max { + max = a[i] + } + } + + c := make([]int, max+1) + for i := range a { + c[a[i]]++ + } + for i := 1; i <= max; i++ { + c[i] += c[i-1] + } + + r := make([]int, n) + for i := range a { + index := c[a[i]] - 1 + r[index] = a[i] + c[a[i]]-- + } + + copy(a, r) +} diff --git a/go/14_sorts/CountingSort_test.go b/go/14_sorts/CountingSort_test.go new file mode 100644 index 00000000..c6f5b466 --- /dev/null +++ b/go/14_sorts/CountingSort_test.go @@ -0,0 +1,13 @@ +package _4_sorts + +import "testing" + +func TestCountingSort(t *testing.T) { + arr := []int{5, 4} + CountingSort(arr, len(arr)) + t.Log(arr) + + arr = []int{5, 4, 3, 2, 1} + CountingSort(arr, len(arr)) + t.Log(arr) +} diff --git a/go/15_binarysearch/binarysearch.go b/go/15_binarysearch/binarysearch.go new file mode 100644 index 00000000..f9c8ff55 --- /dev/null +++ b/go/15_binarysearch/binarysearch.go @@ -0,0 +1,155 @@ +package _5_binarysearch + +func BinarySearch(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) / 2 + if a[mid] == v { + return mid + } else if a[mid] > v { + high = mid - 1 + } else { + low = mid + 1 + } + } + + return -1 +} + +func BinarySearchRecursive(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + return bs(a, v, 0, n-1) +} + +func bs(a []int, v int, low, high int) int { + if low > high { + return -1 + } + + mid := (low + high) / 2 + if a[mid] == v { + return mid + } else if a[mid] > v { + return bs(a, v, low, mid-1) + } else { + return bs(a, v, mid+1, high) + } +} + +//查找第一个等于给定值的元素 +func BinarySearchFirst(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid == 0 || a[mid-1] != v { + return mid + } else { + high = mid - 1 + } + } + } + + return -1 +} + +//查找最后一个值等于给定值的元素 +func BinarySearchLast(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid == n-1 || a[mid+1] != v { + return mid + } else { + low = mid + 1 + } + } + } + + return -1 +} + +//查找第一个大于等于给定值的元素 +func BinarySearchFirstGT(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (high + low) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid != n-1 && a[mid+1] > v { + return mid + 1 + } else { + low = mid + 1 + } + } + } + + return -1 +} + +//查找最后一个小于等于给定值的元素 +func BinarySearchLastLT(a []int, v int) int { + n := len(a) + if n == 0 { + return -1 + } + + low := 0 + high := n - 1 + for low <= high { + mid := (low + high) >> 1 + if a[mid] > v { + high = mid - 1 + } else if a[mid] < v { + low = mid + 1 + } else { + if mid == 0 || a[mid-1] < v { + return mid - 1 + } else { + high = mid - 1 + } + } + } + + return -1 +} diff --git a/go/15_binarysearch/binarysearch_test.go b/go/15_binarysearch/binarysearch_test.go new file mode 100644 index 00000000..2ac76a11 --- /dev/null +++ b/go/15_binarysearch/binarysearch_test.go @@ -0,0 +1,103 @@ +package _5_binarysearch + +import "testing" + +func TestBinarySearch(t *testing.T) { + var a []int + + a = []int{1, 3, 5, 6, 8} + if BinarySearch(a, 8) != 4 { + t.Fatal(BinarySearch(a, 3)) + } + if BinarySearch(a, 4) != -1 { + t.Fatal(BinarySearch(a, 4)) + } +} + +func TestBinarySearchRecursive(t *testing.T) { + var a []int + + a = []int{1, 3, 5, 6, 8} + if BinarySearchRecursive(a, 8) != 4 { + t.Fatal(BinarySearch(a, 3)) + } + if BinarySearchRecursive(a, 4) != -1 { + t.Fatal(BinarySearch(a, 4)) + } +} + +func TestBinarySearchFirst(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirst(a, 2) != 1 { + t.Fatal(BinarySearchFirst(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirst(a, 3) != 4 { + t.Fatal(BinarySearchFirst(a, 3)) + } +} + +func TestBinarySearchLast(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLast(a, 2) != 3 { + t.Fatal(BinarySearchLast(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLast(a, 3) != 4 { + t.Fatal(BinarySearchLast(a, 3)) + } +} + +func TestBinarySearchFirstGT(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 2) != 4 { + t.Fatal(BinarySearchFirstGT(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 1) != 1 { + t.Fatal(BinarySearchFirstGT(a, 1)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 3) != 5 { + t.Fatal(BinarySearchFirstGT(a, 3)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchFirstGT(a, 4) != -1 { + t.Fatal(BinarySearchFirstGT(a, 4)) + } +} + +func TestBinarySearchLastLT(t *testing.T) { + var a []int + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 2) != 0 { + t.Fatal(BinarySearchLastLT(a, 2)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 1) != -1 { + t.Fatal(BinarySearchLastLT(a, 1)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 3) != 3 { + t.Fatal(BinarySearchLastLT(a, 3)) + } + + a = []int{1, 2, 2, 2, 3, 4} + if BinarySearchLastLT(a, 4) != 4 { + t.Fatal(BinarySearchLastLT(a, 4)) + } +} diff --git a/go/17_skiplist/skiplist.go b/go/17_skiplist/skiplist.go new file mode 100644 index 00000000..0938ada9 --- /dev/null +++ b/go/17_skiplist/skiplist.go @@ -0,0 +1,177 @@ +package _7_skiplist + +import ( + "fmt" + "math" + "math/rand" +) + +const ( + //最高层数 + MAX_LEVEL = 16 +) + +//跳表节点结构体 +type skipListNode struct { + //跳表保存的值 + v interface{} + //用于排序的分值 + score int + //层高 + level int + //每层前进指针 + forwards []*skipListNode +} + +//新建跳表节点 +func newSkipListNode(v interface{}, score, level int) *skipListNode { + return &skipListNode{v: v, score: score, forwards: make([]*skipListNode, level, level), level: level} +} + +//跳表结构体 +type SkipList struct { + //跳表头结点 + head *skipListNode + //跳表当前层数 + level int + //跳表长度 + length int +} + +//实例化跳表对象 +func NewSkipList() *SkipList { + //头结点,便于操作 + head := newSkipListNode(0, math.MinInt32, MAX_LEVEL) + return &SkipList{head, 1, 0} +} + +//获取跳表长度 +func (sl *SkipList) Length() int { + return sl.length +} + +//获取跳表层级 +func (sl *SkipList) Level() int { + return sl.level +} + +//插入节点到跳表中 +func (sl *SkipList) Insert(v interface{}, score int) int { + if nil == v { + return 1 + } + + //查找插入位置 + cur := sl.head + //记录每层的路径 + update := [MAX_LEVEL]*skipListNode{} + i := MAX_LEVEL - 1 + for ; i >= 0; i-- { + for nil != cur.forwards[i] { + if cur.forwards[i].v == v { + return 2 + } + if cur.forwards[i].score > score { + update[i] = cur + break + } + cur = cur.forwards[i] + } + if nil == cur.forwards[i] { + update[i] = cur + } + } + + //通过随机算法获取该节点层数 + level := 1 + for i := 1; i < MAX_LEVEL; i++ { + if rand.Int31()%7 == 1 { + level++ + } + } + + //创建一个新的跳表节点 + newNode := newSkipListNode(v, score, level) + + //原有节点连接 + for i := 0; i <= level-1; i++ { + next := update[i].forwards[i] + update[i].forwards[i] = newNode + newNode.forwards[i] = next + } + + //如果当前节点的层数大于之前跳表的层数 + //更新当前跳表层数 + if level > sl.level { + sl.level = level + } + + //更新跳表长度 + sl.length++ + + return 0 +} + +//查找 +func (sl *SkipList) Find(v interface{}, score int) *skipListNode { + if nil == v || sl.length == 0 { + return nil + } + + cur := sl.head + for i := sl.level - 1; i >= 0; i-- { + for nil != cur.forwards[i] { + if cur.forwards[i].score == score && cur.forwards[i].v == v { + return cur.forwards[i] + } else if cur.forwards[i].score > score { + break + } + cur = cur.forwards[i] + } + } + + return nil +} + +//删除节点 +func (sl *SkipList) Delete(v interface{}, score int) int { + if nil == v { + return 1 + } + + //查找前驱节点 + cur := sl.head + //记录前驱路径 + update := [MAX_LEVEL]*skipListNode{} + for i := sl.level - 1; i >= 0; i-- { + update[i] = sl.head + for nil != cur.forwards[i] { + if cur.forwards[i].score == score && cur.forwards[i].v == v { + update[i] = cur + break + } + cur = cur.forwards[i] + } + } + + cur = update[0].forwards[0] + for i := cur.level - 1; i >= 0; i-- { + if update[i] == sl.head && cur.forwards[i] == nil { + sl.level = i + } + + if nil == update[i].forwards[i] { + update[i].forwards[i] = nil + } else { + update[i].forwards[i] = update[i].forwards[i].forwards[i] + } + } + + sl.length-- + + return 0 +} + +func (sl *SkipList) String() string { + return fmt.Sprintf("level:%+v, length:%+v", sl.level, sl.length) +} diff --git a/go/17_skiplist/skiplist_test.go b/go/17_skiplist/skiplist_test.go new file mode 100644 index 00000000..3c04f36f --- /dev/null +++ b/go/17_skiplist/skiplist_test.go @@ -0,0 +1,38 @@ +package _7_skiplist + +import "testing" + +func TestSkipList(t *testing.T) { + sl := NewSkipList() + + sl.Insert("leo", 95) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") + + sl.Insert("jack", 88) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") + + sl.Insert("lily", 100) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") + + t.Log(sl.Find("jack", 88)) + t.Log("-----------------------------") + + sl.Delete("leo", 95) + t.Log(sl.head.forwards[0]) + t.Log(sl.head.forwards[0].forwards[0]) + t.Log(sl.head.forwards[0].forwards[0].forwards[0]) + t.Log(sl) + t.Log("-----------------------------") +} diff --git a/go/20_lru/lru_cache.go b/go/20_lru/lru_cache.go new file mode 100644 index 00000000..4cd826ef --- /dev/null +++ b/go/20_lru/lru_cache.go @@ -0,0 +1,148 @@ +package lru_cache + +const ( + hostbit = uint64(^uint(0)) == ^uint64(0) + LENGTH = 100 +) + +type lruNode struct { + prev *lruNode + next *lruNode + + key int // lru key + value int // lru value + + hnext *lruNode // 拉链 +} + +type LRUCache struct { + node []lruNode // hash list + + head *lruNode // lru head node + tail *lruNode // lru tail node + + capacity int // + used int // +} + +func Constructor(capacity int) LRUCache { + return LRUCache{ + node: make([]lruNode, LENGTH), + head: nil, + tail: nil, + capacity: capacity, + used: 0, + } +} + +func (this *LRUCache) Get(key int) int { + if this.tail == nil { + return -1 + } + + if tmp := this.searchNode(key); tmp != nil { + this.moveToTail(tmp) + return tmp.value + } + return -1 +} + +func (this *LRUCache) Put(key int, value int) { + // 1. 首次插入数据 + // 2. 插入数据不在 LRU 中 + // 3. 插入数据在 LRU 中 + // 4. 插入数据不在 LRU 中, 并且 LRU 已满 + + if tmp := this.searchNode(key); tmp != nil { + tmp.value = value + this.moveToTail(tmp) + return + } + this.addNode(key, value) + + if this.used > this.capacity { + this.delNode() + } +} + +func (this *LRUCache) addNode(key int, value int) { + newNode := &lruNode{ + key: key, + value: value, + } + + tmp := &this.node[hash(key)] + newNode.hnext = tmp.hnext + tmp.hnext = newNode + this.used++ + + if this.tail == nil { + this.tail, this.head = newNode, newNode + return + } + this.tail.next = newNode + newNode.prev = this.tail + this.tail = newNode +} + +func (this *LRUCache) delNode() { + if this.head == nil { + return + } + prev := &this.node[hash(this.head.key)] + tmp := prev.hnext + + for tmp != nil && tmp.key != this.head.key { + prev = tmp + tmp = tmp.hnext + } + if tmp == nil { + return + } + prev.hnext = tmp.hnext + this.head = this.head.next + this.head.prev = nil + this.used-- +} + +func (this *LRUCache) searchNode(key int) *lruNode { + if this.tail == nil { + return nil + } + + // 查找 + tmp := this.node[hash(key)].hnext + for tmp != nil { + if tmp.key == key { + return tmp + } + tmp = tmp.hnext + } + return nil +} + +func (this *LRUCache) moveToTail(node *lruNode) { + if this.tail == node { + return + } + if this.head == node { + this.head = node.next + this.head.prev = nil + } else { + node.next.prev = node.prev + node.prev.next = node.next + } + + node.next = nil + this.tail.next = node + node.prev = this.tail + + this.tail = node +} + +func hash(key int) int { + if hostbit { + return (key ^ (key >> 32)) & (LENGTH - 1) + } + return (key ^ (key >> 16)) & (LENGTH - 1) +} diff --git a/go/20_lru/lru_cache_test.go b/go/20_lru/lru_cache_test.go new file mode 100644 index 00000000..45fc93c5 --- /dev/null +++ b/go/20_lru/lru_cache_test.go @@ -0,0 +1,67 @@ +package lru_cache + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_Hostbit(t *testing.T) { + fmt.Println(hostbit) +} + +func Test_LRU(t *testing.T) { + lru := Constructor(2) + + lru.Put(1, 1) + lru.Put(2, 2) + assert.Equal(t, lru.Get(1), 1) // returns 1 + lru.Put(3, 3) // evicts key 2 + assert.Equal(t, lru.Get(2), -1) // returns -1 (not found) + lru.Put(4, 4) // evicts key 1 + assert.Equal(t, lru.Get(1), -1) // returns -1 (not found) + assert.Equal(t, lru.Get(3), 3) // returns 3 + assert.Equal(t, lru.Get(4), 4) // returns 4 +} + +func Test_LRU_PutGet(t *testing.T) { + lru := Constructor(1) + + lru.Put(1, 2) + assert.Equal(t, lru.Get(1), 2) // returns 2 +} + +func Test_LRU_PutGetPutGetGet(t *testing.T) { + lru := Constructor(1) + + lru.Put(2, 1) + assert.Equal(t, lru.Get(2), 1) // returns 1 + lru.Put(3, 2) + assert.Equal(t, lru.Get(2), -1) // returns -1 + assert.Equal(t, lru.Get(3), 2) // returns 2 +} + +func Test_LRU_PPGPPG(t *testing.T) { + lru := Constructor(2) + + lru.Put(2, 1) + lru.Put(2, 2) + assert.Equal(t, lru.Get(2), 2) // returns 2 + lru.Put(1, 4) + lru.Put(4, 1) + assert.Equal(t, lru.Get(2), -1) // returns -1 + assert.Equal(t, lru.Get(3), -1) // returns -1 +} + +func Test_LRU_PPGPPG_2(t *testing.T) { + lru := Constructor(2) + + lru.Put(2, 1) + lru.Put(2, 2) + assert.Equal(t, lru.Get(2), 2) // returns 2 + lru.Put(1, 1) + lru.Put(4, 1) + assert.Equal(t, lru.Get(2), -1) // returns -1 + assert.Equal(t, lru.Get(3), -1) // returns -1 +} diff --git a/go/23_binarytree/binarytree.go b/go/23_binarytree/binarytree.go new file mode 100644 index 00000000..6a883406 --- /dev/null +++ b/go/23_binarytree/binarytree.go @@ -0,0 +1,66 @@ +package binarytree + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func preOrderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + if root.Left == nil && root.Right == nil { + return []int{root.Val} + } + var stack []*TreeNode + var res []int + stack = append(stack, root) + for len(stack) != 0 { + e := stack[len(stack)-1] + stack = stack[:len(stack)-1] + res = append(res, e.Val) + if e.Right != nil { + stack = append(stack, e.Right) + } + if e.Left != nil { + stack = append(stack, e.Left) + } + } + return res +} + +func inOrderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + if root.Left == nil && root.Right == nil { + return []int{root.Val} + } + res := inOrderTraversal(root.Left) + res = append(res, root.Val) + res = append(res, inOrderTraversal(root.Right)...) + + return res +} + +func postOrderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + var res []int + if root.Left != nil { + lres := postOrderTraversal(root.Left) + if len(lres) > 0 { + res = append(res, lres...) + } + } + if root.Right != nil { + rres := postOrderTraversal(root.Right) + if len(rres) > 0 { + res = append(res, rres...) + } + } + res = append(res, root.Val) + return res +} diff --git a/go/23_binarytree/binarytree_test.go b/go/23_binarytree/binarytree_test.go new file mode 100644 index 00000000..defd222f --- /dev/null +++ b/go/23_binarytree/binarytree_test.go @@ -0,0 +1,94 @@ +package binarytree + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +var tcs = []struct { + pre, in, post []int +}{ + + { + []int{1, 2, 3}, + []int{1, 3, 2}, + []int{3, 2, 1}, + }, + + { + []int{1, 2, 4, 5, 3, 6, 7}, + []int{4, 2, 5, 1, 6, 3, 7}, + []int{4, 5, 2, 6, 7, 3, 1}, + }, + // 可以有多个 testCase +} + +func PreIn2Tree(pre, in []int) *TreeNode { + if len(pre) != len(in) { + panic("preIn2Tree 中两个切片的长度不相等") + } + + if len(in) == 0 { + return nil + } + + res := &TreeNode{ + Val: pre[0], + } + + if len(in) == 1 { + return res + } + + idx := indexOf(res.Val, in) + + res.Left = PreIn2Tree(pre[1:idx+1], in[:idx]) + res.Right = PreIn2Tree(pre[idx+1:], in[idx+1:]) + + return res +} + +func indexOf(val int, nums []int) int { + for i, v := range nums { + if v == val { + return i + } + } + + return 0 +} + +func Test_preOrderTraversal(t *testing.T) { + ast := assert.New(t) + + for _, tc := range tcs { + fmt.Printf("~~%v~~\n", tc) + + root := PreIn2Tree(tc.pre, tc.in) + ast.Equal(tc.pre, preOrderTraversal(root), "输入:%v", tc) + } +} + +func Test_inOrderTraversal(t *testing.T) { + ast := assert.New(t) + + for _, tc := range tcs { + fmt.Printf("~~%v~~\n", tc) + + root := PreIn2Tree(tc.pre, tc.in) + ast.Equal(tc.in, inOrderTraversal(root), "输入:%v", tc) + } +} + +func Test_postOrderTraversal(t *testing.T) { + ast := assert.New(t) + + for _, tc := range tcs { + fmt.Printf("~~%v~~\n", tc) + + root := PreIn2Tree(tc.pre, tc.in) + ast.Equal(tc.post, postOrderTraversal(root), "输入:%v", tc) + } +} diff --git a/go/24_tree/BinarySearchTree.go b/go/24_tree/BinarySearchTree.go new file mode 100644 index 00000000..0e39e35c --- /dev/null +++ b/go/24_tree/BinarySearchTree.go @@ -0,0 +1,145 @@ +package _4_tree + +type BST struct { + *BinaryTree + //比对函数,0:v==nodeV,正数:v>nodeV,负数:v 0 { //v > nodeV + p = p.right + } else { //v < nodeV + p = p.left + } + } + return nil +} + +func (this *BST) Insert(v interface{}) bool { + p := this.root + for nil != p { + compareResult := this.compareFunc(v, p.data) + if compareResult == 0 { + return false + } else if compareResult > 0 { + if nil == p.right { + p.right = NewNode(v) + break + } + p = p.right + } else { + if nil == p.left { + p.left = NewNode(v) + break + } + p = p.left + } + } + return true +} + +func (this *BST) Delete(v interface{}) bool { + var pp *Node = nil + p := this.root + deleteLeft := false + for nil != p { + compareResult := this.compareFunc(v, p.data) + if compareResult > 0 { + pp = p + p = p.right + deleteLeft = false + } else if compareResult < 0 { + pp = p + p = p.left + deleteLeft = true + } else { + break + } + } + + if nil == p { //需要删除的节点不存在 + return false + } else if nil == p.left && nil == p.right { //删除的是一个叶子节点 + if nil != pp { + if deleteLeft { + pp.left = nil + } else { + pp.right = nil + } + } else { //根节点 + this.root = nil + } + } else if nil != p.right { //删除的是一个有右孩子,不一定有左孩子的节点 + //找到p节点右孩子的最小节点 + pq := p + q := p.right //向右走一步 + fromRight := true + for nil != q.left { //向左走到底 + pq = q + q = q.left + fromRight = false + } + if fromRight { + pq.right = nil + } else { + pq.left = nil + } + q.left = p.left + q.right = p.right + if nil == pp { //根节点被删除 + this.root = q + } else { + if deleteLeft { + pq.left = q + } else { + pq.right = q + } + } + } else { //删除的是一个只有左孩子的节点 + if nil != pp { + if deleteLeft { + pp.left = p.left + } else { + pp.right = p.left + } + } else { + if deleteLeft { + this.root = p.left + } else { + this.root = p.left + } + } + } + + return true + +} + +func (this *BST) Min() *Node { + p := this.root + for nil != p.left { + p = p.left + } + return p +} + +func (this *BST) Max() *Node { + p := this.root + for nil != p.right { + p = p.right + } + return p +} diff --git a/go/24_tree/BinarySearchTree_test.go b/go/24_tree/BinarySearchTree_test.go new file mode 100644 index 00000000..f9dbb616 --- /dev/null +++ b/go/24_tree/BinarySearchTree_test.go @@ -0,0 +1,145 @@ +package _4_tree + +import "testing" + +var compareFunc = func(v, nodeV interface{}) int { + vInt := v.(int) + nodeVInt := nodeV.(int) + + if vInt > nodeVInt { + return 1 + } else if vInt < nodeVInt { + return -1 + } + return 0 +} + +func TestBST_Find(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(1) + bst.Insert(2) + bst.Insert(7) + bst.Insert(5) + + t.Log(bst.Find(2)) +} + +func TestBST_Insert(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(1) + bst.Insert(2) + bst.Insert(7) + bst.Insert(5) + + bst.InOrderTraverse() +} + +func TestBST_Min(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(1) + bst.Insert(2) + bst.Insert(7) + bst.Insert(5) + + t.Log(bst.Min()) +} + +func TestBST_Max(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(1) + bst.Insert(2) + bst.Insert(7) + bst.Insert(5) + + t.Log(bst.Max()) +} + +func TestBST_DeleteA(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(2) + bst.Insert(7) + bst.Insert(5) + + t.Log(bst.Delete(7)) + + bst.InOrderTraverse() +} + +func TestBST_DeleteB(t *testing.T) { + bst := NewBST(1, compareFunc) + + t.Log(bst.Delete(1)) + t.Log(bst.root) + + bst.InOrderTraverse() +} + +func TestBST_DeleteC(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(2) + bst.Insert(7) + bst.Insert(5) + + t.Log(bst.Delete(1)) + + bst.InOrderTraverse() +} + +func TestBST_DeleteD(t *testing.T) { + bst := NewBST(1, compareFunc) + + bst.Insert(3) + bst.Insert(2) + bst.Insert(5) + + t.Log(bst.Delete(1)) + + bst.InOrderTraverse() +} +func TestBST_DeleteE(t *testing.T) { + bst := NewBST(5, compareFunc) + + bst.Insert(3) + bst.Insert(2) + bst.Insert(4) + bst.Insert(1) + + t.Log(bst.Delete(5)) + bst.InOrderTraverse() +} + +func TestBST_DeleteF(t *testing.T) { + bst := NewBST(5, compareFunc) + + bst.Insert(3) + bst.Insert(2) + bst.Insert(4) + bst.Insert(1) + + t.Log(bst.Delete(2)) + bst.InOrderTraverse() +} + +func TestBST_DeleteG(t *testing.T) { + bst := NewBST(5, compareFunc) + + bst.Insert(3) + bst.Insert(2) + bst.Insert(4) + bst.Insert(1) + + t.Log(bst.Delete(1)) + bst.InOrderTraverse() +} diff --git a/go/24_tree/BinaryTree.go b/go/24_tree/BinaryTree.go new file mode 100644 index 00000000..cd2417db --- /dev/null +++ b/go/24_tree/BinaryTree.go @@ -0,0 +1,97 @@ +package _4_tree + +import "fmt" + +type BinaryTree struct { + root *Node +} + +func NewBinaryTree(rootV interface{}) *BinaryTree { + return &BinaryTree{NewNode(rootV)} +} + +func (this *BinaryTree) InOrderTraverse() { + p := this.root + s := NewArrayStack() + + for !s.IsEmpty() || nil != p { + if nil != p { + s.Push(p) + p = p.left + } else { + tmp := s.Pop().(*Node) + fmt.Printf("%+v ", tmp.data) + p = tmp.right + } + } + fmt.Println() +} + +func (this *BinaryTree) PreOrderTraverse() { + p := this.root + s := NewArrayStack() + + for !s.IsEmpty() || nil != p { + if nil != p { + fmt.Printf("%+v ", p.data) + s.Push(p) + p = p.left + } else { + p = s.Pop().(*Node).right + } + } + + fmt.Println() +} + +func (this *BinaryTree) PostOrderTraverse() { + s1 := NewArrayStack() + s2 := NewArrayStack() + s1.Push(this.root) + for !s1.IsEmpty() { + p := s1.Pop().(*Node) + s2.Push(p) + if nil != p.left { + s1.Push(p.left) + } + if nil != p.right { + s1.Push(p.right) + } + } + + for !s2.IsEmpty() { + fmt.Printf("%+v ", s2.Pop().(*Node).data) + } +} + +//use one stack, pre cursor to traverse from post order +func (this *BinaryTree) PostOrderTraverse2() { + r := this.root + s := NewArrayStack() + + //point to last visit node + var pre *Node + + s.Push(r) + + for !s.IsEmpty() { + r = s.Top().(*Node) + if (r.left == nil && r.right == nil) || + (pre != nil && (pre == r.left || pre == r.right)) { + + fmt.Printf("%+v ", r.data) + s.Pop() + pre = r + } else { + if r.right != nil { + s.Push(r.right) + } + + if r.left != nil { + s.Push(r.left) + } + + } + + } +} diff --git a/go/24_tree/BinaryTree_test.go b/go/24_tree/BinaryTree_test.go new file mode 100644 index 00000000..6f8bf154 --- /dev/null +++ b/go/24_tree/BinaryTree_test.go @@ -0,0 +1,39 @@ +package _4_tree + +import "testing" + +func TestBinaryTree_InOrderTraverse(t *testing.T) { + binaryTree := NewBinaryTree(1) + binaryTree.root.left = NewNode(3) + binaryTree.root.right = NewNode(4) + binaryTree.root.right.left = NewNode(5) + + binaryTree.InOrderTraverse() +} + +func TestBinaryTree_PreOrderTraverse(t *testing.T) { + binaryTree := NewBinaryTree(1) + binaryTree.root.left = NewNode(3) + binaryTree.root.right = NewNode(4) + binaryTree.root.right.left = NewNode(5) + + binaryTree.PreOrderTraverse() +} + +func TestBinaryTree_PostOrderTraverse(t *testing.T) { + binaryTree := NewBinaryTree(1) + binaryTree.root.left = NewNode(3) + binaryTree.root.right = NewNode(4) + binaryTree.root.right.left = NewNode(5) + + binaryTree.PostOrderTraverse() +} + +func TestBinaryTree_PostOrderTraverse2(t *testing.T) { + binaryTree := NewBinaryTree(1) + binaryTree.root.left = NewNode(3) + binaryTree.root.right = NewNode(4) + binaryTree.root.right.left = NewNode(5) + + binaryTree.PostOrderTraverse2() +} diff --git a/go/24_tree/StackBasedOnArray.go b/go/24_tree/StackBasedOnArray.go new file mode 100644 index 00000000..e1c585f6 --- /dev/null +++ b/go/24_tree/StackBasedOnArray.go @@ -0,0 +1,72 @@ +package _4_tree + +import "fmt" + +/* +基于数组实现的栈 +*/ + +type ArrayStack struct { + //数据 + data []interface{} + //栈顶指针 + top int +} + +func NewArrayStack() *ArrayStack { + return &ArrayStack{ + data: make([]interface{}, 0, 32), + top: -1, + } +} + +func (this *ArrayStack) IsEmpty() bool { + if this.top < 0 { + return true + } + return false +} + +func (this *ArrayStack) Push(v interface{}) { + if this.top < 0 { + this.top = 0 + } else { + this.top += 1 + } + + if this.top > len(this.data)-1 { + this.data = append(this.data, v) + } else { + this.data[this.top] = v + } +} + +func (this *ArrayStack) Pop() interface{} { + if this.IsEmpty() { + return nil + } + v := this.data[this.top] + this.top -= 1 + return v +} + +func (this *ArrayStack) Top() interface{} { + if this.IsEmpty() { + return nil + } + return this.data[this.top] +} + +func (this *ArrayStack) Flush() { + this.top = -1 +} + +func (this *ArrayStack) Print() { + if this.IsEmpty() { + fmt.Println("empty statck") + } else { + for i := this.top; i >= 0; i-- { + fmt.Println(this.data[i]) + } + } +} diff --git a/go/24_tree/TreeNode.go b/go/24_tree/TreeNode.go new file mode 100644 index 00000000..775cf46c --- /dev/null +++ b/go/24_tree/TreeNode.go @@ -0,0 +1,17 @@ +package _4_tree + +import "fmt" + +type Node struct { + data interface{} + left *Node + right *Node +} + +func NewNode(data interface{}) *Node { + return &Node{data: data} +} + +func (this *Node) String() string { + return fmt.Sprintf("v:%+v, left:%+v, right:%+v", this.data, this.left, this.right) +} diff --git a/go/28_heap/heap.go b/go/28_heap/heap.go new file mode 100644 index 00000000..0d9327ee --- /dev/null +++ b/go/28_heap/heap.go @@ -0,0 +1,83 @@ +package heap + +type Heap struct { + a []int + n int + count int +} + +//init heap +func NewHeap(capacity int) *Heap { + heap := &Heap{} + heap.n = capacity + heap.a = make([]int, capacity+1) + heap.count = 0 + return heap +} + +//top-max heap -> heapify from down to up +func (heap *Heap) insert(data int) { + //defensive + if heap.count == heap.n { + return + } + + heap.count++ + heap.a[heap.count] = data + + //compare with parent node + i := heap.count + parent := i / 2 + for parent > 0 && heap.a[parent] < heap.a[i] { + swap(heap.a, parent, i) + i = parent + parent = i / 2 + } +} + +//heapfify from up to down +func (heap *Heap) removeMax() { + + //defensive + if heap.count == 0 { + return + } + + //swap max and last + swap(heap.a, 1, heap.count) + heap.count-- + + //heapify from up to down + heapifyUpToDown(heap.a, heap.count) +} + +//heapify +func heapifyUpToDown(a []int, count int) { + + for i := 1; i <= count/2; { + + maxIndex := i + if a[i] < a[i*2] { + maxIndex = i * 2 + } + + if i*2+1 <= count && a[maxIndex] < a[i*2+1] { + maxIndex = i*2 + 1 + } + + if maxIndex == i { + break + } + + swap(a, i, maxIndex) + i = maxIndex + } + +} + +//swap two elements +func swap(a []int, i int, j int) { + tmp := a[i] + a[i] = a[j] + a[j] = tmp +} diff --git a/go/28_heap/heap_sort.go b/go/28_heap/heap_sort.go new file mode 100644 index 00000000..76171335 --- /dev/null +++ b/go/28_heap/heap_sort.go @@ -0,0 +1,54 @@ +package heap + +//build a heap +func buidHeap(a []int, n int) { + + //heapify from the last parent node + for i := n / 2; i >= 1; i-- { + heapifyUpToDown(a, i, n) + } + +} + +//sort by ascend, a index begin from 1, has n elements +func sort(a []int, n int) { + buidHeap(a, n) + + k := n + for k >= 1 { + swap(a, 1, k) + heapifyUpToDown(a, 1, k-1) + k-- + } +} + +//heapify from up to down , node index = top +func heapifyUpToDown(a []int, top int, count int) { + + for i := top; i <= count/2; { + + maxIndex := i + if a[i] < a[i*2] { + maxIndex = i * 2 + } + + if i*2+1 <= count && a[maxIndex] < a[i*2+1] { + maxIndex = i*2 + 1 + } + + if maxIndex == i { + break + } + + swap(a, i, maxIndex) + i = maxIndex + } + +} + +//swap two elements +func swap(a []int, i int, j int) { + tmp := a[i] + a[i] = a[j] + a[j] = tmp +} diff --git a/go/29_priority_queue/heap.go b/go/29_priority_queue/heap.go new file mode 100644 index 00000000..da1177c2 --- /dev/null +++ b/go/29_priority_queue/heap.go @@ -0,0 +1,22 @@ +package pqueue + +func adjustHeap(src []Node, start, end int) { + if start >= end { + return + } + + // 只需要保证优先级最高的节点在 src[1] 的位置即可 + for i := end / 2; i >= start; i-- { + high := i + if src[high].priority < src[2*i].priority { + high = 2 * i + } + if 2*i+1 <= end && src[high].priority < src[2*i+1].priority { + high = 2*i + 1 + } + if high == i { + continue + } + src[high], src[i] = src[i], src[high] + } +} diff --git a/go/29_priority_queue/heap_test.go b/go/29_priority_queue/heap_test.go new file mode 100644 index 00000000..15018a24 --- /dev/null +++ b/go/29_priority_queue/heap_test.go @@ -0,0 +1,14 @@ +package pqueue + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_AdjustHeap(t *testing.T) { + list := []Node{Node{0, 0}, Node{1, 1}, Node{2, 2}, Node{3, 3}, Node{4, 1}, Node{6, 6}} + + adjustHeap(list, 1, len(list)-1) + assert.Equal(t, 6, list[1].value) +} diff --git a/go/29_priority_queue/priority_queue.go b/go/29_priority_queue/priority_queue.go new file mode 100644 index 00000000..f367591b --- /dev/null +++ b/go/29_priority_queue/priority_queue.go @@ -0,0 +1,62 @@ +package pqueue + +// Node 队列节点 +type Node struct { + value int + priority int +} + +// PQueue priority queue +type PQueue struct { + heap []Node + + capacity int + used int +} + +// NewPriorityQueue new +func NewPriorityQueue(capacity int) PQueue { + return PQueue{ + heap: make([]Node, capacity+1, capacity+1), + capacity: capacity, + used: 0, + } +} + +// Push 入队 +func (q *PQueue) Push(node Node) { + + if q.used > q.capacity { + // 队列已满 + return + } + q.used++ + q.heap[q.used] = node + // 堆化可以放在 Pop 中 + // adjustHeap(q.heap, 1, q.used) +} + +// Pop 出队列 +func (q *PQueue) Pop() Node { + if q.used == 0 { + return Node{-1, -1} + } + // 先堆化, 再取堆顶元素 + adjustHeap(q.heap, 1, q.used) + node := q.heap[1] + + q.heap[1] = q.heap[q.used] + q.used-- + + return node +} + +// Top 获取队列顶部元素 +func (q *PQueue) Top() Node { + if q.used == 0 { + return Node{-1, -1} + } + + adjustHeap(q.heap, 1, q.used) + return q.heap[1] +} diff --git a/go/29_priority_queue/priority_queue_test.go b/go/29_priority_queue/priority_queue_test.go new file mode 100644 index 00000000..5f7bb1e1 --- /dev/null +++ b/go/29_priority_queue/priority_queue_test.go @@ -0,0 +1,74 @@ +package pqueue + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_Push(t *testing.T) { + queue := NewPriorityQueue(100) + + queue.Push(Node{0, 1}) + assert.Equal(t, 0, queue.Top().value) + + queue.Push(Node{3, 1}) + assert.Equal(t, 0, queue.Top().value) + + queue.Push(Node{3, 2}) + assert.Equal(t, 3, queue.Top().value) + + queue.Push(Node{6, 6}) + assert.Equal(t, 6, queue.Top().value) + + queue.Push(Node{12, 5}) + assert.Equal(t, 6, queue.Top().value) + + queue.Push(Node{13, 8}) + assert.Equal(t, 13, queue.Top().value) +} + +func Test_PushPop(t *testing.T) { + queue := NewPriorityQueue(100) + + queue.Push(Node{0, 1}) + queue.Push(Node{3, 1}) + queue.Push(Node{3, 2}) + queue.Push(Node{6, 6}) + queue.Push(Node{12, 5}) + queue.Push(Node{13, 8}) + assert.Equal(t, 13, queue.Top().value) + + assert.Equal(t, 13, queue.Pop().value) + assert.Equal(t, 6, queue.Pop().value) + assert.Equal(t, 12, queue.Top().value) + assert.Equal(t, 12, queue.Pop().value) + + queue.Push(Node{24, 8}) + assert.Equal(t, 24, queue.Top().value) +} + +// 无法保证入队顺序和出队顺序的一致性 +// func Test_PushPop_Equal(t *testing.T) { +// queue := NewPriorityQueue(9) + +// queue.Push(Node{0, 1}) // 8 +// queue.Push(Node{3, 1}) // 9 +// queue.Push(Node{3, 2}) // 3 +// queue.Push(Node{6, 2}) // 4 +// queue.Push(Node{11, 3}) // 2 +// queue.Push(Node{12, 2}) // 5 +// queue.Push(Node{13, 2}) // 6 +// queue.Push(Node{19, 5}) // 1 +// queue.Push(Node{17, 2}) // 7 + +// assert.Equal(t, 19, queue.Pop().value) +// assert.Equal(t, 11, queue.Pop().value) +// assert.Equal(t, 3, queue.Pop().value) +// assert.Equal(t, 6, queue.Pop().value) +// assert.Equal(t, 12, queue.Pop().value) +// assert.Equal(t, 13, queue.Pop().value) +// assert.Equal(t, 17, queue.Pop().value) +// assert.Equal(t, 0, queue.Pop().value) +// assert.Equal(t, 3, queue.Pop().value) +// } diff --git a/go/29_priority_queue/readme.md b/go/29_priority_queue/readme.md new file mode 100644 index 00000000..c3406566 --- /dev/null +++ b/go/29_priority_queue/readme.md @@ -0,0 +1,3 @@ + +## TODO +- 该实现方式不能保证 相同优先级的元素在出队列时 和入队列的顺序是一致的 \ No newline at end of file diff --git a/go/31_graph/graph_search.go b/go/31_graph/graph_search.go new file mode 100644 index 00000000..d5f61955 --- /dev/null +++ b/go/31_graph/graph_search.go @@ -0,0 +1,152 @@ +package graph + +import ( + "container/list" + "fmt" +) + +//adjacency table, 无向图 +type Graph struct { + adj []*list.List + v int +} + +//init graphh according to capacity +func newGraph(v int) *Graph { + graphh := &Graph{} + graphh.v = v + graphh.adj = make([]*list.List, v) + for i := range graphh.adj { + graphh.adj[i] = list.New() + } + return graphh +} + +//insert as add edge,一条边存2次 +func (self *Graph) addEdge(s int, t int) { + self.adj[s].PushBack(t) + self.adj[t].PushBack(s) +} + +//search path by BFS +func (self *Graph) BFS(s int, t int) { + + //todo + if s == t { + return + } + + //init prev + prev := make([]int, self.v) + for index := range prev { + prev[index] = -1 + } + + //search by queue + var queue []int + visited := make([]bool, self.v) + queue = append(queue, s) + visited[s] = true + isFound := false + for len(queue) > 0 && !isFound { + top := queue[0] + queue = queue[1:] + linkedlist := self.adj[top] + for e := linkedlist.Front(); e != nil; e = e.Next() { + k := e.Value.(int) + if !visited[k] { + prev[k] = top + if k == t { + isFound = true + break + } + queue = append(queue, k) + visited[k] = true + } + } + } + + if isFound { + printPrev(prev, s, t) + } else { + fmt.Printf("no path found from %d to %d\n", s, t) + } + +} + +//search by DFS +func (self *Graph) DFS(s int, t int) { + + prev := make([]int, self.v) + for i := range prev { + prev[i] = -1 + } + + visited := make([]bool, self.v) + visited[s] = true + + isFound := false + self.recurse(s, t, prev, visited, isFound) + + printPrev(prev, s, t) +} + +//recursivly find path +func (self *Graph) recurse(s int, t int, prev []int, visited []bool, isFound bool) { + + if isFound { + return + } + + visited[s] = true + + if s == t { + isFound = true + return + } + + linkedlist := self.adj[s] + for e := linkedlist.Front(); e != nil; e = e.Next() { + k := e.Value.(int) + if !visited[k] { + prev[k] = s + self.recurse(k, t, prev, visited, false) + } + } + +} + +//print path recursively +func printPrev(prev []int, s int, t int) { + + if t == s || prev[t] == -1 { + fmt.Printf("%d ", t) + } else { + printPrev(prev, s, prev[t]) + fmt.Printf("%d ", t) + } + +} + +//func main() { +// graph := newGraph(8) +// graph.addEdge(0, 1) +// graph.addEdge(0, 3) +// graph.addEdge(1, 2) +// graph.addEdge(1, 4) +// graph.addEdge(2, 5) +// graph.addEdge(3, 4) +// graph.addEdge(4, 5) +// graph.addEdge(4, 6) +// graph.addEdge(5, 7) +// graph.addEdge(6, 7) +// +// graph.BFS(0, 7) +// fmt.Println() +// graph.BFS(1, 3) +// fmt.Println() +// graph.DFS(0, 7) +// fmt.Println() +// graph.DFS(1, 3) +// fmt.Println() +//} diff --git a/go/32_string/string_bf.go b/go/32_string/string_bf.go new file mode 100644 index 00000000..edc37286 --- /dev/null +++ b/go/32_string/string_bf.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" +) + +//BF search pattern index, return the first match subs start index +func bfSearch(main string, pattern string) int { + + //defensive + if len(main) == 0 || len(pattern) == 0 || len(main) < len(pattern) { + return -1 + } + + for i := 0; i <= len(main)-len(pattern); i++ { + subStr := main[i : i+len(pattern)] + if subStr == pattern { + return i + } + } + + return -1 +} + +func main() { + + main := "abcd227fac" + pattern := "ac" + fmt.Println(bfSearch(main, pattern)) +} diff --git a/go/32_string/string_bm.go b/go/32_string/string_bm.go new file mode 100644 index 00000000..c64f7e6a --- /dev/null +++ b/go/32_string/string_bm.go @@ -0,0 +1,140 @@ +package main + +import ( + "fmt" + "math" +) + +//bc: pattern char index hash mapping +func generateBC(pattern string) []int { + + bc := make([]int, 256) + + for index := range bc { + bc[index] = -1 + } + + for index, char := range pattern { + bc[int(char)] = index + } + + return bc +} + +//generate suffix and prefix array for pattern +func generateGS(pattern string) ([]int, []bool) { + m := len(pattern) + suffix := make([]int, m) + prefix := make([]bool, m) + + //init + for i := 0; i < m; i++ { + suffix[i] = -1 + prefix[i] = false + } + + for i := 0; i < m-1; i++ { + j := i + k := 0 + for j >= 0 && pattern[j] == pattern[m-1-k] { + j-- + k++ + suffix[k] = j + 1 + } + + if j == -1 { + prefix[k] = true + } + } + + return suffix, prefix +} + +//todo +func moveByGS(patternLength int, badCharStartIndex int, suffix []int, prefix []bool) int { + + //length of good suffix + k := patternLength - badCharStartIndex - 1 + + //complete match + if suffix[k] != -1 { + return badCharStartIndex + 1 - suffix[k] + } + + //partial match + for t := patternLength - 1; t > badCharStartIndex+1; t-- { + if prefix[t] { + return t + } + } + + //no match + return patternLength + +} + +func bmSearch(main string, pattern string) int { + //defensive + if len(main) == 0 || len(pattern) == 0 || len(pattern) > len(main) { + return -1 + } + + bc := generateBC(pattern) + suffix, prefix := generateGS(pattern) + + n := len(main) + m := len(pattern) + + // i : start index of main string + step := 1 + for i := 0; i <= n-m; i = i + step { + subStr := main[i : i+m] + k, j := findBadChar(subStr, pattern, bc) + + stepForBC := j - k + //j is bad char occur index + if j == -1 { + return i + } + + stepForGS := -1 + if j < m-1 { + stepForGS = moveByGS(m, j, suffix, prefix) + } + + //k is bad char index in pattern + step = int(math.Max(float64(stepForBC), float64(stepForGS))) + } + + return -1 +} + +func findBadChar(subStr string, pattern string, bc []int) (int, int) { + + j := -1 + k := -1 + badChar := rune(0) + + for index := len(subStr) - 1; index >= 0; index-- { + if subStr[index] != pattern[index] { + j = index + badChar = rune(subStr[index]) + break + } + } + + //if bad character exist, then find it's index at pattern + if j > 0 { + k = bc[int(badChar)] + } + + return k, j +} + +func main() { + + main := "abcacabcbcabcabc" + pattern := "cabcab" + + fmt.Println(bmSearch(main, pattern)) +} diff --git a/go/34_kmp/kmp.go b/go/34_kmp/kmp.go new file mode 100644 index 00000000..75941f16 --- /dev/null +++ b/go/34_kmp/kmp.go @@ -0,0 +1,73 @@ +package main + +import ( + "fmt" +) + +func getNexts(pattern string) []int { + m := len(pattern) + nexts := make([]int, m) + for index := range nexts { + nexts[index] = -1 + } + + for i := 1; i < m - 1; i++ { + j := nexts[i - 1] + + for pattern[j + 1] != pattern[i] && j >= 0 { + j = nexts[j] + } + + if pattern[j + 1] == pattern[i] { + j += 1 + } + + nexts[i] = j + } + + return nexts +} + +func findByKMP(s string, pattern string) int { + n := len(s) + m := len(pattern) + if n < m { + return -1 + } + + nexts := getNexts(pattern) + + j := 0 + for i := 0; i < n; i++ { + for j > 0 && s[i] != pattern[j] { + j = nexts[j - 1] + 1 + } + + if s[i] == pattern[j] { + if j == m - 1 { + return i - m + 1 + } + j += 1 + } + } + + return -1 +} + +func main(){ + s := "abc abcdab abcdabcdabde" + pattern := "bcdabd" + fmt.Println(findByKMP(s, pattern)) //16 + + s = "aabbbbaaabbababbabbbabaaabb" + pattern = "abab" + fmt.Println(findByKMP(s, pattern)) //11 + + s = "aabbbbaaabbababbabbbabaaabb" + pattern = "ababacd" + fmt.Println(findByKMP(s, pattern)) //-1 + + s = "hello" + pattern = "ll" + fmt.Println(findByKMP(s, pattern)) //2 +} \ No newline at end of file diff --git a/go/41_dynamic_programming/backtracking/leastcoins.go b/go/41_dynamic_programming/backtracking/leastcoins.go new file mode 100644 index 00000000..ba242cb0 --- /dev/null +++ b/go/41_dynamic_programming/backtracking/leastcoins.go @@ -0,0 +1,102 @@ +package backtracking + +import ( + "fmt" +) + +const intMax = int(^uint(0) >> 1) + +var Cnt int + +// LeastCoins find least number of coins of which the total values are equals a given one +func LeastCoins(targetTotal int, coinOptions []int) int { + minNum := intMax + + memo := make([][]int, targetTotal+1) + for i := range memo { + memo[i] = make([]int, len(coinOptions)) + } + fmt.Println("start") + leastCoins(&minNum, 0, targetTotal, len(coinOptions)-1, coinOptions, memo) + fmt.Println("end") + + return minNum + +} + +func leastCoins(minNum *int, cNum, totalValue, opIndex int, coinOptions []int, memo [][]int) { + Cnt++ + if 0 == totalValue { + if cNum < *minNum { + *minNum = cNum + } + + return + } + + if opIndex < 0 { + return + } + + num4Option := 0 + remaining := totalValue - coinOptions[opIndex]*num4Option + for remaining >= 0 { + + if opIndex != 0 { + if shouldSkip(memo, remaining, opIndex-1, cNum+num4Option) { + goto Next + } + } + leastCoins(minNum, cNum+num4Option, remaining, opIndex-1, coinOptions, memo) + + Next: + num4Option++ + remaining = totalValue - coinOptions[opIndex]*num4Option + + } + +} + +func shouldSkip(memo [][]int, totalValue, nextOpIdex, cNum int) bool { + if memo[totalValue][nextOpIdex] > 0 && memo[totalValue][nextOpIdex] <= cNum { + fmt.Printf("skip,%d, %d as %d <= %d \n", totalValue, nextOpIdex, memo[totalValue][nextOpIdex], cNum) + return true + } + if memo[totalValue][nextOpIdex] == 0 || memo[totalValue][nextOpIdex] > cNum { + memo[totalValue][nextOpIdex] = cNum + } + return false +} + +func LeastCoins2(targetTotal int, coinOptions []int) int { + + minNum := intMax + memo := make([][]bool, targetTotal) + for i := range memo { + memo[i] = make([]bool, targetTotal/coinOptions[0]) + } + + fmt.Println("start") + leastCoins2(&minNum, targetTotal, coinOptions, 0, 0, memo) + fmt.Println("end") + + return minNum + +} + +func leastCoins2(minNum *int, targetTotal int, coinOptions []int, cNum, cValue int, memo [][]bool) { + Cnt++ + if cValue == targetTotal { + if *minNum > cNum { + *minNum = cNum + } + return + } + + for _, coin := range coinOptions { + if coin+cValue <= targetTotal && !memo[cValue+coin-1][cNum] { + memo[cValue+coin-1][cNum] = true + leastCoins2(minNum, targetTotal, coinOptions, cNum+1, cValue+coin, memo) + } + } +} diff --git a/go/41_dynamic_programming/backtracking/leastcoins_test.go b/go/41_dynamic_programming/backtracking/leastcoins_test.go new file mode 100644 index 00000000..672d72ba --- /dev/null +++ b/go/41_dynamic_programming/backtracking/leastcoins_test.go @@ -0,0 +1,56 @@ +package backtracking + +import "testing" + +func TestFindLeastCoins(t *testing.T) { + + coinOptions := []int{1, 3, 5, 10, 50} + + Cnt = 0 + result := LeastCoins(9, coinOptions) + + t.Log("test 1 =====================") + if result != 3 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + + Cnt = 0 + t.Log("test 2 =====================") + result = LeastCoins(36, coinOptions) + + if result != 5 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + +} + +func TestFindLeastCoins2(t *testing.T) { + + coinOptions := []int{1, 3, 5, 10, 50} + + Cnt = 0 + result := LeastCoins2(9, coinOptions) + + t.Log("test 1 =====================") + if result != 3 { + t.Logf("least coins %d", result) + t.Error("failed") + } + + t.Logf("cnt===%d", Cnt) + + Cnt = 0 + t.Log("test 2 =====================") + result = LeastCoins2(36, coinOptions) + + if result != 5 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + +} diff --git a/go/41_dynamic_programming/dp/leastcoins.go b/go/41_dynamic_programming/dp/leastcoins.go new file mode 100644 index 00000000..394e2637 --- /dev/null +++ b/go/41_dynamic_programming/dp/leastcoins.go @@ -0,0 +1,63 @@ +package dp + +var Cnt int + +// minNum(v) = 1 + min(minNum(v-i)) +func LeastCoins(targetTotal int, coinOptions []int) int { + memo := make([]int, targetTotal) + + cMinNum := leastCoins(targetTotal, coinOptions, memo) + + return cMinNum + +} + +// State Transition Table +func LeastCoins2(targetTotal int, coinOptions []int) int { + memo := make([]int, targetTotal) + + for i := 1; i <= targetTotal; i++ { + + minNum := -1 + for _, coin := range coinOptions { + Cnt++ + if i < coin { + break + } + if i == coin { + minNum = 1 + break + } + if minNum == -1 || minNum > memo[i-coin-1]+1 { + minNum = memo[i-coin-1] + 1 + } + } + memo[i-1] = minNum + } + + return memo[targetTotal-1] + +} + +func leastCoins(targetTotal int, coinOptions, memo []int) int { + Cnt++ + if targetTotal == 0 { + return 0 + } + if memo[targetTotal-1] != 0 { + return memo[targetTotal-1] + } + cMinNum := -1 + for _, coin := range coinOptions { + if targetTotal-coin < 0 { + continue + } + cNum := 1 + leastCoins(targetTotal-coin, coinOptions, memo) + if cMinNum == -1 || cNum < cMinNum { + cMinNum = cNum + } + + } + memo[targetTotal-1] = cMinNum + return cMinNum +} diff --git a/go/41_dynamic_programming/dp/leastcoins_test.go b/go/41_dynamic_programming/dp/leastcoins_test.go new file mode 100644 index 00000000..b1560ee6 --- /dev/null +++ b/go/41_dynamic_programming/dp/leastcoins_test.go @@ -0,0 +1,55 @@ +package dp + +import "testing" + +func TestFindLeastCoins(t *testing.T) { + + coinOptions := []int{1, 3, 5, 10, 50} + + Cnt = 0 + result := LeastCoins(9, coinOptions) + + t.Log("test 1 =====================") + if result != 3 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + + Cnt = 0 + t.Log("test 2 =====================") + result = LeastCoins(36, coinOptions) + + if result != 5 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + +} + +func TestFindLeastCoins2(t *testing.T) { + + coinOptions := []int{1, 3, 5, 10, 50} + + Cnt = 0 + result := LeastCoins2(9, coinOptions) + + t.Log("test 1 =====================") + if result != 3 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + + Cnt = 0 + t.Log("test 2 =====================") + result = LeastCoins2(36, coinOptions) + + if result != 5 { + t.Logf("least coins %d", result) + t.Error("failed") + } + t.Logf("cnt===%d", Cnt) + +} diff --git a/go/42_dynamic_programming/longest_common_substring.go b/go/42_dynamic_programming/longest_common_substring.go new file mode 100644 index 00000000..55a4fbf6 --- /dev/null +++ b/go/42_dynamic_programming/longest_common_substring.go @@ -0,0 +1,42 @@ +package main + +import "fmt" + +func lsc(s1 string, s2 string) int { + m := len(s1) + n := len(s2) + + memo := make([][]int, m + 1) + for i := 0; i < m + 1; i++ { + memo[i] = make([]int, n + 1) + } + + + for i := 1; i < m + 1; i++ { + for j := 1; j < n + 1; j++ { + if s1[i - 1] == s2[j - 1] { + memo[i][j] = memo[i - 1][j - 1] + 1 + } + } + } + + fmt.Println(memo) + longest := 0 + for i, _ := range memo { + for j, e2 := range memo[i] { + if longest < memo[i][j] { + longest = e2 + } + } + } + + return longest +} + +func main() { + fmt.Println(lsc("blue", "clues")) //3 + fmt.Println(lsc("fosh", "fish")) //2 + fmt.Println(lsc("fosh", "fort")) //2 + fmt.Println(lsc("hish", "fish")) //3 + fmt.Println(lsc("hish", "vista")) //2 +} \ No newline at end of file diff --git a/go/45_bitmap/bitmap.go b/go/45_bitmap/bitmap.go new file mode 100644 index 00000000..7f036855 --- /dev/null +++ b/go/45_bitmap/bitmap.go @@ -0,0 +1,29 @@ +package bitmap + +// BitMap implement bitmap +type BitMap []byte + +// New create BitMap +func New(length uint) BitMap { + return make([]byte, length/8+1) +} + +// Set set value in bitmap +func (b BitMap) Set(value uint) { + byteIndex := value / 8 + if byteIndex >= uint(len(b)) { + return + } + bitIndex := value % 8 + []byte(b)[byteIndex] |= 1 << bitIndex +} + +// Get check whether value exist or not +func (b BitMap) Get(value uint) bool { + byteIndex := value / 8 + if byteIndex >= uint(len(b)) { + return false + } + bitIndex := value % 8 + return []byte(b)[byteIndex]&(1<> 1) + if nums[mid] > value { + end = mid - 1 + } else if nums[mid] < value { + start = mid + 1 + } else { + for mid >= 0 { + if nums[mid-1] == value { + mid-- + } else { + return mid + } + } + } + } + return -1 +} + +// 查找最后一个值等于给定值的元素 +func BinarySearch3(nums []int, value int) int { + start := 0 + end := len(nums) - 1 + for start <= end { + mid := start + ((end - start) >> 1) + if nums[mid] > value { + end = mid - 1 + } else if nums[mid] < value { + start = mid + 1 + } else { + for mid < len(nums) { + if nums[mid+1] == value { + mid++ + } else { + return mid + } + } + } + } + return -1 +} + +// 查找第一个大于等于给定值的元素 +func BinarySearch4(nums []int, value int) int { + start := 0 + end := len(nums) - 1 + for start <= end { + mid := (start + end) >> 1 + if nums[mid] < value { + start = mid + 1 + } else { + for mid >= 0 { + if nums[mid-1] >= value { + mid-- + } else { + return mid + } + } + } + } + return -1 +} + +// 查找最后一个小于等于给定值的元素 +func BinarySearch5(nums []int, value int) int { + start := 0 + end := len(nums) - 1 + for start <= end { + mid := (start + end) >> 1 + if nums[mid] > value { + end = mid - 1 + } else { + for mid < len(nums) { + if nums[mid+1] <= value { + mid++ + } else { + return mid + } + } + } + } + return -1 +} diff --git a/java/05_array/Array.java b/java/05_array/Array.java index 6b7ad055..e69fd607 100644 --- a/java/05_array/Array.java +++ b/java/05_array/Array.java @@ -5,20 +5,21 @@ * 2)数组中的数据是int类型的; * * Author: Zheng + * modify: xing, Gsealy */ public class Array { //定义整型数据data保存数据 public int data[]; //定义数组长度 private int n; - //定义中保存的数据个数 + //定义中实际个数 private int count; //构造方法,定义数组大小 public Array(int capacity){ - this.data = new int[]{0,1,2,3,4}; + this.data = new int[capacity]; this.n = capacity; - this.count=capacity; + this.count=0;//一开始一个数都没有存所以为0 } //根据索引,找到数据中的元素并返回 @@ -27,6 +28,35 @@ public int find(int index){ return data[index]; } + //插入元素:头部插入,尾部插入 + public boolean insert(int index, int value){ + //数组中无元素 + + //if (index == count && count == 0) { + // data[index] = value; + // ++count; + // return true; + //} + + // 数组空间已满 + if (count == n) { + System.out.println("没有可插入的位置"); + return false; + } + // 如果count还没满,那么就可以插入数据到数组中 + // 位置不合法 + if (index < 0||index > count ) { + System.out.println("位置不合法"); + return false; + } + // 位置合法 + for( int i = count; i > index; --i){ + data[i] = data[i - 1]; + } + data[index] = value; + ++count; + return true; + } //根据索引,删除数组中元素 public boolean delete(int index){ if (index<0 || index >=count) return false; @@ -41,42 +71,9 @@ public boolean delete(int index){ } this.data = arr;*/ - --count; return true; } - - //向数组中插入一个元素 - public boolean insert(int index, int value){ - if (index<0 || index>=count) return false; -// if (count == n) return false;不是太懂 - //数组长度增加1 - int[] arr = new int[count+1]; - for (int i = 0; i < data.length; i++) { - arr[i] = data[i]; - } - data=arr; - - for (int i = count-1; i>=index; --i){ - data[i+1] = data[i]; - } - data[index] = value; - ++count; - return true; - } - - public boolean insertToTail(int value) { -// if (count == n) return false;不是太懂 - //数组长度增加1 - int[] arr = new int[count+1]; - for (int i = 0; i < data.length; i++) { - arr[i] = data[i]; - } - data=arr; - data[count++] = value; - return true; - } - public void printAll() { for (int i = 0; i < count; ++i) { System.out.print(data[i] + " "); @@ -84,6 +81,15 @@ public void printAll() { System.out.println(); } - - + public static void main(String[] args) { + Array array = new Array(5); + array.printAll(); + array.insert(0, 3); + array.insert(0, 4); + array.insert(1, 5); + array.insert(3, 9); + array.insert(3, 10); + //array.insert(3, 11); + array.printAll(); + } } diff --git a/java/05_array/GenericArray.java b/java/05_array/GenericArray.java new file mode 100644 index 00000000..631a1bae --- /dev/null +++ b/java/05_array/GenericArray.java @@ -0,0 +1,163 @@ +public class GenericArray { + private T[] data; + private int size; + + // 根据传入容量,构造Array + public GenericArray(int capacity) { + data = (T[]) new Object[capacity]; + size = 0; + } + + // 无参构造方法,默认数组容量为10 + public GenericArray() { + this(10); + } + + // 获取数组容量 + public int getCapacity() { + return data.length; + } + + // 获取当前元素个数 + public int count() { + return size; + } + + // 判断数组是否为空 + public boolean isEmpty() { + return size == 0; + } + + // 修改 index 位置的元素 + public void set(int index, T e) { + checkIndex(index); + data[index] = e; + } + + // 获取对应 index 位置的元素 + public T get(int index) { + checkIndex(index); + return data[index]; + } + + // 查看数组是否包含元素e + public boolean contains(T e) { + for (int i = 0; i < size; i++) { + if (data[i].equals(e)) { + return true; + } + } + return false; + } + + // 获取对应元素的下标, 未找到,返回 -1 + public int find(T e) { + for ( int i = 0; i < size; i++) { + if (data[i].equals(e)) { + return i; + } + } + return -1; + } + + + // 在 index 位置,插入元素e, 时间复杂度 O(m+n) + public void add(int index, T e) { + checkIndex(index); + // 如果当前元素个数等于数组容量,则将数组扩容为原来的2倍 + if (size == data.length) { + resize(2 * data.length); + } + + for (int i = size - 1; i >= index; i--) { + data[i + 1] = data[i]; + } + data[index] = e; + size++; + } + + // 向数组头插入元素 + public void addFirst(T e) { + add(0, e); + } + + // 向数组尾插入元素 + public void addLast(T e) { + add(size, e); + } + + // 删除 index 位置的元素,并返回 + public T remove(int index) { + checkIndexForRemove(index); + + T ret = data[index]; + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size --; + data[size] = null; + + // 缩容 + if (size == data.length / 4 && data.length / 2 != 0) { + resize(data.length / 2); + } + + return ret; + } + + // 删除第一个元素 + public T removeFirst() { + return remove(0); + } + + // 删除末尾元素 + public T removeLast() { + return remove(size - 1); + } + + // 从数组中删除指定元素 + public void removeElement(T e) { + int index = find(e); + if (index != -1) { + remove(index); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(String.format("Array size = %d, capacity = %d \n", size, data.length)); + builder.append('['); + for (int i = 0; i < size; i++) { + builder.append(data[i]); + if (i != size - 1) { + builder.append(", "); + } + } + builder.append(']'); + return builder.toString(); + } + + + // 扩容方法,时间复杂度 O(n) + private void resize(int capacity) { + T[] newData = (T[]) new Object[capacity]; + + for (int i = 0; i < size; i++) { + newData[i] = data[i]; + } + data = newData; + } + + private void checkIndex(int index) { + if (index < 0 || index > size) { + throw new IllegalArgumentException("Add failed! Require index >=0 and index <= size."); + } + } + + private void checkIndexForRemove(int index) { + if(index < 0 || index >= size) { + throw new IllegalArgumentException("remove failed! Require index >=0 and index < size."); + } + } +} \ No newline at end of file diff --git a/java/06_linkedlist/LRUBaseLinkedList.java b/java/06_linkedlist/LRUBaseLinkedList.java new file mode 100644 index 00000000..57d94f3b --- /dev/null +++ b/java/06_linkedlist/LRUBaseLinkedList.java @@ -0,0 +1,176 @@ +package linked.singlelist; + + +import java.util.Scanner; + +/** + * 基于单链表LRU算法(java) + * + * @author hoda + * @create 2018-12-17 + */ +public class LRUBaseLinkedList { + + /** + * 默认链表容量 + */ + private final static Integer DEFAULT_CAPACITY = 10; + + /** + * 头结点 + */ + private SNode headNode; + + /** + * 链表长度 + */ + private Integer length; + + /** + * 链表容量 + */ + private Integer capacity; + + public LRUBaseLinkedList() { + this.headNode = new SNode<>(); + this.capacity = DEFAULT_CAPACITY; + this.length = 0; + } + + public LRUBaseLinkedList(Integer capacity) { + this.headNode = new SNode<>(); + this.capacity = capacity; + this.length = 0; + } + + public void add(T data) { + SNode preNode = findPreNode(data); + + // 链表中存在,删除原数据,再插入到链表的头部 + if (preNode != null) { + deleteElemOptim(preNode); + intsertElemAtBegin(data); + } else { + if (length >= this.capacity) { + //删除尾结点 + deleteElemAtEnd(); + } + intsertElemAtBegin(data); + } + } + + /** + * 删除preNode结点下一个元素 + * + * @param preNode + */ + private void deleteElemOptim(SNode preNode) { + SNode temp = preNode.getNext(); + preNode.setNext(temp.getNext()); + temp = null; + length--; + } + + /** + * 链表头部插入节点 + * + * @param data + */ + private void intsertElemAtBegin(T data) { + SNode next = headNode.getNext(); + headNode.setNext(new SNode(data, next)); + length++; + } + + /** + * 获取查找到元素的前一个结点 + * + * @param data + * @return + */ + private SNode findPreNode(T data) { + SNode node = headNode; + while (node.getNext() != null) { + if (data.equals(node.getNext().getElement())) { + return node; + } + node = node.getNext(); + } + return null; + } + + /** + * 删除尾结点 + */ + private void deleteElemAtEnd() { + SNode ptr = headNode; + // 空链表直接返回 + if (ptr.getNext() == null) { + return; + } + + // 倒数第二个结点 + while (ptr.getNext().getNext() != null) { + ptr = ptr.getNext(); + } + + SNode tmp = ptr.getNext(); + ptr.setNext(null); + tmp = null; + length--; + } + + private void printAll() { + SNode node = headNode.getNext(); + while (node != null) { + System.out.print(node.getElement() + ","); + node = node.getNext(); + } + System.out.println(); + } + + public class SNode { + + private T element; + + private SNode next; + + public SNode(T element) { + this.element = element; + } + + public SNode(T element, SNode next) { + this.element = element; + this.next = next; + } + + public SNode() { + this.next = null; + } + + public T getElement() { + return element; + } + + public void setElement(T element) { + this.element = element; + } + + public SNode getNext() { + return next; + } + + public void setNext(SNode next) { + this.next = next; + } + } + + public static void main(String[] args) { + LRUBaseLinkedList list = new LRUBaseLinkedList(); + Scanner sc = new Scanner(System.in); + while (true) { + list.add(sc.nextInt()); + list.printAll(); + } + } +} diff --git a/java/06_linkedlist/LRUBasedArray.java b/java/06_linkedlist/LRUBasedArray.java new file mode 100644 index 00000000..37436f7b --- /dev/null +++ b/java/06_linkedlist/LRUBasedArray.java @@ -0,0 +1,174 @@ +package linkedlist; + +import java.util.HashMap; +import java.util.Map; +/** + * Created by SpecialYang in 2018/12/7 2:00 PM. + * + * 基于数组实现的LRU缓存 + * 1. 空间复杂度为O(n) + * 2. 时间复杂度为O(n) + * 3. 不支持null的缓存 + */ +public class LRUBasedArray { + + private static final int DEFAULT_CAPACITY = (1 << 3); + + private int capacity; + + private int count; + + private T[] value; + + private Map holder; + + public LRUBasedArray() { + this(DEFAULT_CAPACITY); + } + + public LRUBasedArray(int capacity) { + this.capacity = capacity; + value = (T[]) new Object[capacity]; + count = 0; + holder = new HashMap(capacity); + } + + /** + * 模拟访问某个值 + * @param object + */ + public void offer(T object) { + if (object == null) { + throw new IllegalArgumentException("该缓存容器不支持null!"); + } + Integer index = holder.get(object); + if (index == null) { + if (isFull()) { + removeAndCache(object); + } else { + cache(object, count); + } + } else { + update(index); + } + } + + /** + * 若缓存中有指定的值,则更新位置 + * @param end + */ + public void update(int end) { + T target = value[end]; + rightShift(end); + value[0] = target; + holder.put(target, 0); + } + + /** + * 缓存数据到头部,但要先右移 + * @param object + * @param end 数组右移的边界 + */ + public void cache(T object, int end) { + rightShift(end); + value[0] = object; + holder.put(object, 0); + count++; + } + + /** + * 缓存满的情况,踢出后,再缓存到数组头部 + * @param object + */ + public void removeAndCache(T object) { + T key = value[--count]; + holder.remove(key); + cache(object, count); + } + + /** + * end左边的数据统一右移一位 + * @param end + */ + private void rightShift(int end) { + for (int i = end - 1; i >= 0; i--) { + value[i + 1] = value[i]; + holder.put(value[i], i + 1); + } + } + + public boolean isContain(T object) { + return holder.containsKey(object); + } + + public boolean isEmpty() { + return count == 0; + } + + public boolean isFull() { + return count == capacity; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) { + sb.append(value[i]); + sb.append(" "); + } + return sb.toString(); + } + + static class TestLRUBasedArray { + + public static void main(String[] args) { + testDefaultConstructor(); + testSpecifiedConstructor(4); +// testWithException(); + } + + private static void testWithException() { + LRUBasedArray lru = new LRUBasedArray(); + lru.offer(null); + } + + public static void testDefaultConstructor() { + System.out.println("======无参测试========"); + LRUBasedArray lru = new LRUBasedArray(); + lru.offer(1); + lru.offer(2); + lru.offer(3); + lru.offer(4); + lru.offer(5); + System.out.println(lru); + lru.offer(6); + lru.offer(7); + lru.offer(8); + lru.offer(9); + System.out.println(lru); + } + + public static void testSpecifiedConstructor(int capacity) { + System.out.println("======有参测试========"); + LRUBasedArray lru = new LRUBasedArray(capacity); + lru.offer(1); + System.out.println(lru); + lru.offer(2); + System.out.println(lru); + lru.offer(3); + System.out.println(lru); + lru.offer(4); + System.out.println(lru); + lru.offer(2); + System.out.println(lru); + lru.offer(4); + System.out.println(lru); + lru.offer(7); + System.out.println(lru); + lru.offer(1); + System.out.println(lru); + lru.offer(2); + System.out.println(lru); + } + } +} diff --git a/java/06_linkedlist/SinglyLinkedList.java b/java/06_linkedlist/SinglyLinkedList.java index 0931d133..1a8b0f87 100644 --- a/java/06_linkedlist/SinglyLinkedList.java +++ b/java/06_linkedlist/SinglyLinkedList.java @@ -8,160 +8,330 @@ */ public class SinglyLinkedList { - private Node head = null; + private Node head = null; - public Node findByValue(int value) { - Node p = head; - while (p != null && p.data != value) { - p = p.next; + public Node findByValue(int value) { + Node p = head; + while (p != null && p.data != value) { + p = p.next; + } + + return p; } - return p; - } + public Node findByIndex(int index) { + Node p = head; + int pos = 0; + while (p != null && pos != index) { + p = p.next; + ++pos; + } - public Node findByIndex(int index) { - Node p = head; - int pos = 0; - while (p != null && pos != index) { - p = p.next; - ++pos; + return p; } - return p; - } + //无头结点 + //表头部插入 + //这种操作将于输入的顺序相反,逆序 + public void insertToHead(int value) { + Node newNode = new Node(value, null); + insertToHead(newNode); + } - public void insertToHead(int value) { - Node newNode = new Node(value, null); - insertToHead(newNode); - } + public void insertToHead(Node newNode) { + if (head == null) { + head = newNode; + } else { + newNode.next = head; + head = newNode; + } + } - public void insertToHead(Node newNode) { - if (head == null) { - head = newNode; - } else { - newNode.next = head; - head = newNode; + //顺序插入 + //链表尾部插入 + public void insertTail(int value){ + + Node newNode = new Node(value, null); + //空链表,可以插入新节点作为head,也可以不操作 + if (head == null){ + head = newNode; + + }else{ + Node q = head; + while(q.next != null){ + q = q.next; + } + newNode.next = q.next; + q.next = newNode; + } + } + public void insertAfter(Node p, int value) { + Node newNode = new Node(value, null); + insertAfter(p, newNode); } - } - public void insertAfter(Node p, int value) { - Node newNode = new Node(value, null); - insertAfter(p, newNode); - } + public void insertAfter(Node p, Node newNode) { + if (p == null) return; - public void insertAfter(Node p, Node newNode) { - if (p == null) return; + newNode.next = p.next; + p.next = newNode; + } - newNode.next = p.next; - p.next = newNode; - } + public void insertBefore(Node p, int value) { + Node newNode = new Node(value, null); + insertBefore(p, newNode); + } - public void insertBefore(Node p, int value) { - Node newNode = new Node(value, null); - insertBefore(p, newNode); - } + public void insertBefore(Node p, Node newNode) { + if (p == null) return; + if (head == p) { + insertToHead(newNode); + return; + } - public void insertBefore(Node p, Node newNode) { - if (p == null) return; - if (head == p) { - insertToHead(newNode); - return; - } + Node q = head; + while (q != null && q.next != p) { + q = q.next; + } - Node q = head; - while (q != null && q.next != p) { - q = q.next; - } + if (q == null) { + return; + } + + newNode.next = p; + q.next = newNode; - if (q == null) { - return; } - newNode.next = p; - q.next = newNode; + public void deleteByNode(Node p) { + if (p == null || head == null) return; - } + if (p == head) { + head = head.next; + return; + } - public void deleteByNode(Node p) { - if (p == null || head == null) return; + Node q = head; + while (q != null && q.next != p) { + q = q.next; + } - if (p == head) { - head = head.next; - return; + if (q == null) { + return; + } + + q.next = q.next.next; } - Node q = head; - while (q != null && q.next != p) { - q = q.next; + public void deleteByValue(int value) { + if (head == null) return; + + Node p = head; + Node q = null; + while (p != null && p.data != value) { + q = p; + p = p.next; + } + + if (p == null) return; + + if (q == null) { + head = head.next; + } else { + q.next = q.next.next; + } + + // 可重复删除指定value的代码 + /* + if (head != null && head.data == value) { + head = head.next; + } + + Node pNode = head; + while (pNode != null) { + if (pNode.next.data == data) { + pNode.next = pNode.next.next; + continue; + } + pNode = pNode.next; + } + */ } - if (q == null) { - return; + public void printAll() { + Node p = head; + while (p != null) { + System.out.print(p.data + " "); + p = p.next; + } + System.out.println(); } - q.next = q.next.next; - } + //判断true or false + public boolean TFResult(Node left, Node right){ + Node l = left; + Node r = right; + + System.out.println("left_:"+l.data); + System.out.println("right_:"+r.data); + while(l != null && r != null){ + if (l.data == r.data){ + l = l.next; + r = r.next; + continue; + }else{ + break; + } + + } + + System.out.println("什么结果"); + if (l==null && r==null){ + System.out.println("什么结果"); + return true; + }else{ + return false; + } + } + // 判断是否为回文 + + public boolean palindrome(){ + if (head == null){ + return false; + }else{ + System.out.println("开始执行找到中间节点"); + Node p = head; + Node q = head; + if (p.next == null){ + System.out.println("只有一个元素"); + return true; + } + while( q.next != null && q.next.next != null){ + p = p.next; + q = q.next.next; + + } + + System.out.println("中间节点" + p.data); + System.out.println("开始执行奇数节点的回文判断"); + Node leftLink = null; + Node rightLink = null; + if(q.next == null){ + // p 一定为整个链表的中点,且节点数目为奇数 + rightLink = p.next; + leftLink = inverseLinkList(p).next; + System.out.println("左边第一个节点"+leftLink.data); + System.out.println("右边第一个节点"+rightLink.data); + + }else{ + //p q 均为中点 + rightLink = p.next; + leftLink = inverseLinkList(p); + } + return TFResult(leftLink, rightLink); + + } + } - public void deleteByValue(int value) { - if (head == null) return; + //带结点的链表翻转 + public Node inverseLinkList_head(Node p){ + // Head 为新建的一个头结点 + Node Head = new Node(9999,null); + // p 为原来整个链表的头结点,现在Head指向 整个链表 + Head.next = p; + /* + 带头结点的链表翻转等价于 + 从第二个元素开始重新头插法建立链表 + */ + Node Cur = p.next; + p.next = null; + Node next = null; + + while(Cur != null){ + next = Cur.next; + Cur.next = Head.next; + Head.next = Cur; + System.out.println("first " + Head.data); + + Cur = next; + } + + // 返回左半部分的中点之前的那个节点 + // 从此处开始同步像两边比较 + return Head; - Node p = head; - Node q = null; - while (p != null && p.data != value) { - q = p; - p = p.next; } - if (p == null) return; + //无头结点的链表翻转 + public Node inverseLinkList(Node p){ - if (q == null) { - head = head.next; - } else { - q.next = q.next.next; - } + Node pre = null; + Node r = head; + System.out.println("z---" + r.data); + Node next= null; + while(r !=p){ + next = r.next; - // 可重复删除指定value的代码 - /* - if (head != null && head.data == value) { - head = head.next; - } + r.next = pre; + pre = r; + r = next; + } - Node pNode = head; - while (pNode != null) { - if (pNode.next.data == data) { - pNode.next = pNode.next.next; - continue; - } - pNode = pNode.next; - } - */ - } + r.next = pre; + // 返回左半部分的中点之前的那个节点 + // 从此处开始同步像两边比较 + return r; - public void printAll() { - Node p = head; - while (p != null) { - System.out.print(p.data + " "); - p = p.next; } - System.out.println(); - } + + public static Node createNode(int value) { + return new Node(value, null); + } - public static Node createNode(int value) { - return new Node(value, null); - } + public static class Node { + private int data; + private Node next; - public static class Node { - private int data; - private Node next; + public Node(int data, Node next) { + this.data = data; + this.next = next; + } - public Node(int data, Node next) { - this.data = data; - this.next = next; + public int getData() { + return data; + } } - - public int getData() { - return data; + + public static void main(String[]args){ + + SinglyLinkedList link = new SinglyLinkedList(); + System.out.println("hello"); + //int data[] = {1}; + //int data[] = {1,2}; + //int data[] = {1,2,3,1}; + //int data[] = {1,2,5}; + //int data[] = {1,2,2,1}; + // int data[] = {1,2,5,2,1}; + int data[] = {1,2,5,3,1}; + + for(int i =0; i < data.length; i++){ + //link.insertToHead(data[i]); + link.insertTail(data[i]); + } + // link.printAll(); + // Node p = link.inverseLinkList_head(link.head); + // while(p != null){ + // System.out.println("aa"+p.data); + // p = p.next; + // } + + System.out.println("打印原始:"); + link.printAll(); + if (link.palindrome()){ + System.out.println("回文"); + }else{ + System.out.println("不是回文"); + } } - } } diff --git a/java/07_linkedlist/LinkedListAlgo.java b/java/07_linkedlist/LinkedListAlgo.java index 0fce5d4c..be25d80d 100644 --- a/java/07_linkedlist/LinkedListAlgo.java +++ b/java/07_linkedlist/LinkedListAlgo.java @@ -13,21 +13,14 @@ public class LinkedListAlgo { // 单链表反转 public static Node reverse(Node list) { - Node headNode = null; - - Node previousNode = null; - Node currentNode = list; - while (currentNode != null) { - Node nextNode = currentNode.next; - if (nextNode == null) { - headNode = currentNode; - } - currentNode.next = previousNode; - previousNode = currentNode; - currentNode = nextNode; + Node curr = list, pre = null; + while (curr != null) { + Node next = curr.next; + curr.next = pre; + pre = curr; + curr = next; } - - return headNode; + return pre; } // 检测环 @@ -48,41 +41,74 @@ public static boolean checkCircle(Node list) { } // 有序链表合并 - public static Node mergeSortedLists(Node la, Node lb) { - if (la == null) return lb; - if (lb == null) return la; - - Node p = la; - Node q = lb; - Node head; - if (p.data < q.data) { - head = p; - p = p.next; - } else { - head = q; - q = q.next; - } - Node r = head; - - while (p != null && q != null) { - if (p.data < q.data) { - r.next = p; - p = p.next; - } else { - r.next = q; - q = q.next; - } - r = r.next; + // public static Node mergeSortedLists(Node la, Node lb) { + // if (la == null) return lb; + // if (lb == null) return la; + + // Node p = la; + // Node q = lb; + // Node head; + // if (p.data < q.data) { + // head = p; + // p = p.next; + // } else { + // head = q; + // q = q.next; + // } + // Node r = head; + + // while (p != null && q != null) { + // if (p.data < q.data) { + // r.next = p; + // p = p.next; + // } else { + // r.next = q; + // q = q.next; + // } + // r = r.next; + // } + + // if (p != null) { + // r.next = p; + // } else { + // r.next = q; + // } + + // return head; + //} + + //----------------------------------------- + + // 有序链表合并 Leetcode 21 + /** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode soldier = new ListNode(0); //利用哨兵结点简化实现难度 技巧三 + ListNode p = soldier; + + while ( l1 != null && l2 != null ){ + if ( l1.val < l2.val ){ + p.next = l1; + l1 = l1.next; + } + else{ + p.next = l2; + l2 = l2.next; + } + p = p.next; + } + + if (l1 != null) { p.next = l1; } + if (l2 != null) { p.next = l2; } + return soldier.next; } - if (p != null) { - r.next = p; - } else { - r.next = q; - } - - return head; - } // 删除倒数第K个结点 public static Node deleteLastKth(Node list, int k) { @@ -118,7 +144,7 @@ public static Node findMiddleNode(Node list) { Node fast = list; Node slow = list; - while (fast.next != null && fast.next.next != null) { + while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } diff --git a/java/08_stack/StackBasedOnLinkedList.java b/java/08_stack/StackBasedOnLinkedList.java index b078d372..58b25f0a 100644 --- a/java/08_stack/StackBasedOnLinkedList.java +++ b/java/08_stack/StackBasedOnLinkedList.java @@ -5,7 +5,7 @@ * * Author: Zheng */ -public class StackBasedLinkedList { +public class StackBasedOnLinkedList { private Node top = null; public void push(int value) { diff --git a/java/09_queue/CircularQueue.java b/java/09_queue/CircularQueue.java index c3c54cc7..71a988c4 100644 --- a/java/09_queue/CircularQueue.java +++ b/java/09_queue/CircularQueue.java @@ -36,7 +36,8 @@ public String dequeue() { } public void printAll() { - for (int i = head; i < tail; ++i) { + if (0 == n) return; + for (int i = head; i % n != tail; ++i) { System.out.print(items[i] + " "); } System.out.println(); diff --git a/java/09_queue/DynimacArrayQueue.java b/java/09_queue/DynamicArrayQueue.java similarity index 94% rename from java/09_queue/DynimacArrayQueue.java rename to java/09_queue/DynamicArrayQueue.java index 3a19fa3d..9faad0fd 100644 --- a/java/09_queue/DynimacArrayQueue.java +++ b/java/09_queue/DynamicArrayQueue.java @@ -3,7 +3,7 @@ /** * Created by wangzheng on 2018/10/9. */ -public class DynimacArrayQueue { +public class DynamicArrayQueue { // 数组:items,数组大小:n private String[] items; private int n = 0; @@ -12,7 +12,7 @@ public class DynimacArrayQueue { private int tail = 0; // 申请一个大小为capacity的数组 - public DynimacArrayQueue(int capacity) { + public DynamicArrayQueue(int capacity) { items = new String[capacity]; n = capacity; } diff --git a/java/11_sorts/InsertionSortAdd.java b/java/11_sorts/InsertionSortAdd.java new file mode 100644 index 00000000..31c436ea --- /dev/null +++ b/java/11_sorts/InsertionSortAdd.java @@ -0,0 +1,48 @@ +package sorts; + +import java.util.Arrays; + +/** + * 插入排序(插入位置,从头至尾搜索) + * @Author: ooooor + */ +public class InsertionSortAdd { + + public static void main(String[] args) { + int[] data = new int[]{4, 6, 5, 3, 7, 1, 2}; + fromStartToEnd(Arrays.copyOf(data, data.length)); + System.out.println(Arrays.toString(data)); + } + + /** + * 查询插入位置时, 从头至尾搜索 + * @param data + */ + private static void fromStartToEnd(int[] data) { + for (int i=1; i < data.length; i++) { + int value = data[i]; + + int[] tmp = new int[2]; + int change = i; + for (int j=0; j < i; j++) { + if(value >= data[j]) { + continue; + } + + int index = j%2; + if (change == i) { + tmp[Math.abs(index-1)] = data[j]; + change = j; + } + tmp[index] = data[j+1]; + if (0 == index) { + data[j+1] = tmp[index+1]; + } else { + data[j+1] = tmp[index-1]; + } + } + data[change] = value; + } + } + +} diff --git a/java/11_sorts/Sorts.java b/java/11_sorts/Sorts.java new file mode 100644 index 00000000..cbd610a3 --- /dev/null +++ b/java/11_sorts/Sorts.java @@ -0,0 +1,106 @@ +package sorts; + +import java.util.Arrays; + +/** + * 冒泡排序、插入排序、选择排序 + *

+ * Author: Zheng + */ +public class Sorts { + + // 冒泡排序,a是数组,n表示数组大小 + public static void bubbleSort(int[] a, int n) { + if (n <= 1) return; + + for (int i = 0; i < n; ++i) { + // 提前退出标志位 + boolean flag = false; + for (int j = 0; j < n - i - 1; ++j) { + if (a[j] > a[j + 1]) { // 交换 + int tmp = a[j]; + a[j] = a[j + 1]; + a[j + 1] = tmp; + // 此次冒泡有数据交换 + flag = true; + } + } + if (!flag) break; // 没有数据交换,提前退出 + } + } + + /** + * 冒泡排序改进:在每一轮排序后记录最后一次元素交换的位置,作为下次比较的边界, + * 对于边界外的元素在下次循环中无需比较. + */ + public static void bubbleSort2(int[] a, int n) { + if (n <= 1) return; + + // 最后一次交换的位置 + int lastExchange = 0; + // 无序数据的边界,每次只需要比较到这里即可退出 + int sortBorder = n - 1; + for (int i = 0; i < n; i++) { + // 提前退出标志位 + boolean flag = false; + for (int j = 0; j < sortBorder; j++) { + if (a[j] > a[j + 1]) { + int tmp = a[j]; + a[j] = a[j + 1]; + a[j + 1] = tmp; + // 此次冒泡有数据交换 + flag = true; + // 更新最后一次交换的位置 + lastExchange = j; + } + } + sortBorder = lastExchange; + if (!flag) break; // 没有数据交换,提前退出 + } + } + + // 插入排序,a表示数组,n表示数组大小 + public static void insertionSort(int[] a, int n) { + if (n <= 1) return; + + for (int i = 1; i < n; ++i) { + int value = a[i]; + int j = i - 1; + // 查找要插入的位置并移动数据 + for (; j >= 0; --j) { + if (a[j] > value) { + a[j + 1] = a[j]; + } else { + break; + } + } + a[j + 1] = value; + } + } + + // 选择排序,a表示数组,n表示数组大小 + public static void selectionSort(int[] a, int n) { + if (n <= 1) return; + + for (int i = 0; i < n - 1; ++i) { + // 查找最小值 + int minIndex = i; + for (int j = i + 1; j < n; ++j) { + if (a[j] < a[minIndex]) { + minIndex = j; + } + } + + // 交换 + int tmp = a[i]; + a[i] = a[minIndex]; + a[minIndex] = tmp; + } + } + + public static void main(String[] args) { + int[] array = new int[]{3, 4, 2, 1, 5, 6, 7, 8}; + bubbleSort2(array, array.length); + System.out.println(Arrays.toString(array)); + } +} diff --git a/java/11_sorts/SortsAddOn.java b/java/11_sorts/SortsAddOn.java new file mode 100644 index 00000000..397ceb2c --- /dev/null +++ b/java/11_sorts/SortsAddOn.java @@ -0,0 +1,100 @@ +package sorts; + +/** + * 向下冒泡算法 (或许比冒泡更易懂的排序算法?) + * 希尔排序 + * + * Author: wliu + */ +public class SortsAddOn { + + public static void main(String[] args) { + int[] arr = {3, 2, 6, 4, 5, 1, 9, 20, 13, 16}; + // bubbleDownSort(arr); + shellSort(arr); + print(arr); + } + + /** + * 向下冒泡。可能比冒泡更易懂? + * + * 算法概要: + * 从0开始,用这个元素去跟后面的所有元素比较,如果发现这个元素大于后面的某个元素,则交换。 + * 3 2 6 4 5 1 + * 第一趟是从 index=0 也就是 3, 开始跟index=1及其后面的数字比较 + * 3 大于 2,交换,变为 2 3 6 4 5 1,此时index=0的位置变为了2 + * 接下来将用2跟index=2比较 + * 2 不大于 6 不交换 + * 2 不大于 4 不交换 + * 2 不大于 5 不交换 + * 2 大于 1,交换,变为 1 3 6 4 5 2,第一趟排序完成。 + * + * 第二趟是从 index=1 也就是 3,开始跟index=2及其后面的数字比较 + * 3 不大于 6 不交换 + * 3 不大于 4 不交换 + * 3 不大于 5 不交换 + * 3 大于 2,交换,变为 1 2 6 4 5 3,第二趟排序完成。 + * + * 第三趟是从 index=2 也就是 6,开始跟index=3及其后面的数字比较 + * 6 大于 4,交换,变为 1 2 4 6 5 3, 此时 index = 2 的位置变为了4 + * 接下来将用4跟index=4比较 + * 4 不大于 5 不交换 + * 4 大于 3,交换,变为 1 2 3 6 5 4,第三趟排序完成。 + * + * 第四趟是从 index=3 也就是 6,开始跟index=4及其后面的数字比较 + * 6 大于 5,交换,变为 1 2 3 5 6 4, 此时 index = 3 的位置变为了5 + * 接下来将用5跟index=5比较 + * 5 大于 4,交换,变为 1 2 3 4 6 5, 第四趟排序完成。 + * + * 第五趟是从 index=4 也就是 6,开始跟index=5及其后面的数字比较 + * 6 大于 5,交换,变为 1 2 3 4 5 6, 此时 index = 4 的位置变为了5 + * 接下来将用5跟index=6比较 + * index = 6 已经不满足 index < length 的条件,整个排序完成。 + */ + private static void bubbleDownSort(int[] arr) { + int len = arr.length; + if (len == 1) return; + + for (int i = 0; i < len; i++) { + for (int j = i + 1; j < len; j++) { + if (arr[i] > arr[j]) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + } + } + } + + + private static void shellSort(int[] arr) { + int len = arr.length; + if (len == 1) return; + + int step = len / 2; + while (step >= 1) { + for (int i = step; i < len; i++) { + int value = arr[i]; + int j = i - step; + for (; j >= 0; j -= step) { + if (value < arr[j]) { + arr[j+step] = arr[j]; + } else { + break; + } + } + arr[j+step] = value; + } + + step = step / 2; + } + } + + private static void print(int[] arr) { + System.out.println("Print array:"); + for (int x : arr) { + System.out.print(x + "\t"); + } + System.out.println(""); + } +} diff --git a/java/12_sorts/KthSmallest.java b/java/12_sorts/KthSmallest.java new file mode 100644 index 00000000..a6d97429 --- /dev/null +++ b/java/12_sorts/KthSmallest.java @@ -0,0 +1,52 @@ +package sort; + +/** + * @author wangjunwei87 + * @since 2019-03-10 + */ +public class KthSmallest { + + public static int kthSmallest(int[] arr, int k) { + if (arr == null || arr.length < k) { + return -1; + } + + int partition = partition(arr, 0, arr.length - 1); + while (partition + 1 != k) { + if (partition + 1 < k) { + partition = partition(arr, partition + 1, arr.length - 1); + } else { + partition = partition(arr, 0, partition - 1); + } + } + + return arr[partition]; + } + + private static int partition(int[] arr, int p, int r) { + int pivot = arr[r]; + + int i = p; + for (int j = p; j < r; j++) { + // 这里要是 <= ,不然会出现死循环,比如查找数组 [1,1,2] 的第二小的元素 + if (arr[j] <= pivot) { + swap(arr, i, j); + i++; + } + } + + swap(arr, i, r); + + return i; + } + + private static void swap(int[] arr, int i, int j) { + if (i == j) { + return; + } + + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/java/12_sorts/MergeSort.java b/java/12_sorts/MergeSort.java new file mode 100644 index 00000000..3a80a760 --- /dev/null +++ b/java/12_sorts/MergeSort.java @@ -0,0 +1,96 @@ +package sorts; + +/** + * Created by wangzheng on 2018/10/16. + */ +public class MergeSort { + + // 归并排序算法, a是数组,n表示数组大小 + public static void mergeSort(int[] a, int n) { + mergeSortInternally(a, 0, n-1); + } + + // 递归调用函数 + private static void mergeSortInternally(int[] a, int p, int r) { + // 递归终止条件 + if (p >= r) return; + + // 取p到r之间的中间位置q,防止(p+r)的和超过int类型最大值 + int q = p + (r - p)/2; + // 分治递归 + mergeSortInternally(a, p, q); + mergeSortInternally(a, q+1, r); + + // 将A[p...q]和A[q+1...r]合并为A[p...r] + merge(a, p, q, r); + } + + private static void merge(int[] a, int p, int q, int r) { + int i = p; + int j = q+1; + int k = 0; // 初始化变量i, j, k + int[] tmp = new int[r-p+1]; // 申请一个大小跟a[p...r]一样的临时数组 + while (i<=q && j<=r) { + if (a[i] <= a[j]) { + tmp[k++] = a[i++]; // i++等于i:=i+1 + } else { + tmp[k++] = a[j++]; + } + } + + // 判断哪个子数组中有剩余的数据 + int start = i; + int end = q; + if (j <= r) { + start = j; + end = r; + } + + // 将剩余的数据拷贝到临时数组tmp + while (start <= end) { + tmp[k++] = a[start++]; + } + + // 将tmp中的数组拷贝回a[p...r] + for (i = 0; i <= r-p; ++i) { + a[p+i] = tmp[i]; + } + } + + /** + * 合并(哨兵) + * + * @param arr + * @param p + * @param q + * @param r + */ + private static void mergeBySentry(int[] arr, int p, int q, int r) { + int[] leftArr = new int[q - p + 2]; + int[] rightArr = new int[r - q + 1]; + + for (int i = 0; i <= q - p; i++) { + leftArr[i] = arr[p + i]; + } + // 第一个数组添加哨兵(最大值) + leftArr[q - p + 1] = Integer.MAX_VALUE; + + for (int i = 0; i < r - q; i++) { + rightArr[i] = arr[q + 1 + i]; + } + // 第二个数组添加哨兵(最大值) + rightArr[r-q] = Integer.MAX_VALUE; + + int i = 0; + int j = 0; + int k = p; + while (k <= r) { + // 当左边数组到达哨兵值时,i不再增加,直到右边数组读取完剩余值,同理右边数组也一样 + if (leftArr[i] <= rightArr[j]) { + arr[k++] = leftArr[i++]; + } else { + arr[k++] = rightArr[j++]; + } + } + } +} diff --git a/java/12_sorts/QuickSort.java b/java/12_sorts/QuickSort.java new file mode 100644 index 00000000..08136570 --- /dev/null +++ b/java/12_sorts/QuickSort.java @@ -0,0 +1,44 @@ +package sorts; + +/** + * Created by wangzheng on 2018/10/16. + */ +public class QuickSort { + + // 快速排序,a是数组,n表示数组的大小 + public static void quickSort(int[] a, int n) { + quickSortInternally(a, 0, n-1); + } + + // 快速排序递归函数,p,r为下标 + private static void quickSortInternally(int[] a, int p, int r) { + if (p >= r) return; + + int q = partition(a, p, r); // 获取分区点 + quickSortInternally(a, p, q-1); + quickSortInternally(a, q+1, r); + } + + private static int partition(int[] a, int p, int r) { + int pivot = a[r]; + int i = p; + for(int j = p; j < r; ++j) { + if (a[j] < pivot) { + if (i == j) { + ++i; + } else { + int tmp = a[i]; + a[i++] = a[j]; + a[j] = tmp; + } + } + } + + int tmp = a[i]; + a[i] = a[r]; + a[r] = tmp; + + System.out.println("i=" + i); + return i; + } +} diff --git a/java/13_sorts/BucketSort.java b/java/13_sorts/BucketSort.java new file mode 100644 index 00000000..32f9c55a --- /dev/null +++ b/java/13_sorts/BucketSort.java @@ -0,0 +1,130 @@ +/** + * @Description:桶排序算法 + * @Author: Hoda + * @Date: Create in 2019-06-01 + * @Modified By: + * @Modified Date: + */ +public class BucketSort { + + /** + * 桶排序 + * + * @param arr 数组 + * @param bucketSize 桶容量 + */ + public static void bucketSort(int[] arr, int bucketSize) { + if (arr.length < 2) { + return; + } + + // 数组最小值 + int minValue = arr[0]; + // 数组最大值 + int maxValue = arr[1]; + for (int i = 0; i < arr.length; i++) { + if (arr[i] < minValue) { + minValue = arr[i]; + } else if (arr[i] > maxValue) { + maxValue = arr[i]; + } + } + + // 桶数量 + int bucketCount = (maxValue - minValue) / bucketSize + 1; + int[][] buckets = new int[bucketCount][bucketSize]; + int[] indexArr = new int[bucketCount]; + + // 将数组中值分配到各个桶里 + for (int i = 0; i < arr.length; i++) { + int bucketIndex = (arr[i] - minValue) / bucketSize; + if (indexArr[bucketIndex] == buckets[bucketIndex].length) { + ensureCapacity(buckets, bucketIndex); + } + buckets[bucketIndex][indexArr[bucketIndex]++] = arr[i]; + } + + // 对每个桶进行排序,这里使用了快速排序 + int k = 0; + for (int i = 0; i < buckets.length; i++) { + if (indexArr[i] == 0) { + continue; + } + quickSortC(buckets[i], 0, indexArr[i] - 1); + for (int j = 0; j < indexArr[i]; j++) { + arr[k++] = buckets[i][j]; + } + } + } + + /** + * 数组扩容 + * + * @param buckets + * @param bucketIndex + */ + private static void ensureCapacity(int[][] buckets, int bucketIndex) { + int[] tempArr = buckets[bucketIndex]; + int[] newArr = new int[tempArr.length * 2]; + for (int j = 0; j < tempArr.length; j++) { + newArr[j] = tempArr[j]; + } + buckets[bucketIndex] = newArr; + } + + /** + * 快速排序递归函数 + * + * @param arr + * @param p + * @param r + */ + private static void quickSortC(int[] arr, int p, int r) { + if (p >= r) { + return; + } + + int q = partition(arr, p, r); + quickSortC(arr, p, q - 1); + quickSortC(arr, q + 1, r); + } + + /** + * 分区函数 + * + * @param arr + * @param p + * @param r + * @return 分区点位置 + */ + private static int partition(int[] arr, int p, int r) { + int pivot = arr[r]; + int i = p; + for (int j = p; j < r; j++) { + if (arr[j] <= pivot) { + swap(arr, i, j); + i++; + } + } + + swap(arr, i, r); + return i; + } + + /** + * 交换 + * + * @param arr + * @param i + * @param j + */ + private static void swap(int[] arr, int i, int j) { + if (i == j) { + return; + } + + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/java/13_sorts/CountingSort.java b/java/13_sorts/CountingSort.java new file mode 100644 index 00000000..dab6251d --- /dev/null +++ b/java/13_sorts/CountingSort.java @@ -0,0 +1,50 @@ +package sorts; + +/** + * 计数排序 + * + * Author: ZHENG + */ +public class CountingSort { + + // 计数排序,a是数组,n是数组大小。假设数组中存储的都是非负整数。 + public static void countingSort(int[] a, int n) { + if (n <= 1) return; + + // 查找数组中数据的范围 + int max = a[0]; + for (int i = 1; i < n; ++i) { + if (max < a[i]) { + max = a[i]; + } + } + + // 申请一个计数数组c,下标大小[0,max] + int[] c = new int[max + 1]; + + // 计算每个元素的个数,放入c中 + for (int i = 0; i < n; ++i) { + c[a[i]]++; + } + + // 依次累加 + for (int i = 1; i < max + 1; ++i) { + c[i] = c[i-1] + c[i]; + } + + // 临时数组r,存储排序之后的结果 + int[] r = new int[n]; + // 计算排序的关键步骤了,有点难理解 + for (int i = n - 1; i >= 0; --i) { + int index = c[a[i]]-1; + r[index] = a[i]; + c[a[i]]--; + } + + // 将结果拷贝会a数组 + for (int i = 0; i < n; ++i) { + a[i] = r[i]; + } + } + +} diff --git a/java/13_sorts/RadixSort.java b/java/13_sorts/RadixSort.java new file mode 100644 index 00000000..2c07d3e0 --- /dev/null +++ b/java/13_sorts/RadixSort.java @@ -0,0 +1,62 @@ +/** + * @Description:基数排序 + * @Author: Hoda + * @Date: Create in 2019-07-25 + * @Modified By: + * @Modified Date: + */ +public class RadixSort { + + /** + * 基数排序 + * + * @param arr + */ + public static void radixSort(int[] arr) { + int max = arr[0]; + for (int i = 0; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + } + + // 从个位开始,对数组arr按"指数"进行排序 + for (int exp = 1; max / exp > 0; exp *= 10) { + countingSort(arr, exp); + } + } + + /** + * 计数排序-对数组按照"某个位数"进行排序 + * + * @param arr + * @param exp 指数 + */ + public static void countingSort(int[] arr, int exp) { + if (arr.length <= 1) { + return; + } + + // 计算每个元素的个数 + int[] c = new int[10]; + for (int i = 0; i < arr.length; i++) { + c[(arr[i] / exp) % 10]++; + } + + // 计算排序后的位置 + for (int i = 1; i < c.length; i++) { + c[i] += c[i - 1]; + } + + // 临时数组r,存储排序之后的结果 + int[] r = new int[arr.length]; + for (int i = arr.length - 1; i >= 0; i--) { + r[c[(arr[i] / exp) % 10] - 1] = arr[i]; + c[(arr[i] / exp) % 10]--; + } + + for (int i = 0; i < arr.length; i++) { + arr[i] = r[i]; + } + } +} diff --git a/java/17_skiplist/SkipList.java b/java/17_skiplist/SkipList.java new file mode 100644 index 00000000..9ffff488 --- /dev/null +++ b/java/17_skiplist/SkipList.java @@ -0,0 +1,128 @@ +package skiplist; + +import java.util.Random; + +/** + * 跳表的一种实现方法。 + * 跳表中存储的是正整数,并且存储的是不重复的。 + * + * Author:ZHENG + */ +public class SkipList { + + private static final int MAX_LEVEL = 16; + + private int levelCount = 1; + + private Node head = new Node(); // 带头链表 + + private Random r = new Random(); + + public Node find(int value) { + Node p = head; + for (int i = levelCount - 1; i >= 0; --i) { + while (p.forwards[i] != null && p.forwards[i].data < value) { + p = p.forwards[i]; + } + } + + if (p.forwards[0] != null && p.forwards[0].data == value) { + return p.forwards[0]; + } else { + return null; + } + } + + public void insert(int value) { + int level = randomLevel(); + Node newNode = new Node(); + newNode.data = value; + newNode.maxLevel = level; + Node update[] = new Node[level]; + for (int i = 0; i < level; ++i) { + update[i] = head; + } + + // record every level largest value which smaller than insert value in update[] + Node p = head; + for (int i = level - 1; i >= 0; --i) { + while (p.forwards[i] != null && p.forwards[i].data < value) { + p = p.forwards[i]; + } + update[i] = p;// use update save node in search path + } + + // in search path node next node become new node forwords(next) + for (int i = 0; i < level; ++i) { + newNode.forwards[i] = update[i].forwards[i]; + update[i].forwards[i] = newNode; + } + + // update node hight + if (levelCount < level) levelCount = level; + } + + public void delete(int value) { + Node[] update = new Node[levelCount]; + Node p = head; + for (int i = levelCount - 1; i >= 0; --i) { + while (p.forwards[i] != null && p.forwards[i].data < value) { + p = p.forwards[i]; + } + update[i] = p; + } + + if (p.forwards[0] != null && p.forwards[0].data == value) { + for (int i = levelCount - 1; i >= 0; --i) { + if (update[i].forwards[i] != null && update[i].forwards[i].data == value) { + update[i].forwards[i] = update[i].forwards[i].forwards[i]; + } + } + } + + while (levelCount>1&&head.forwards[levelCount]==null){ + levelCount--; + } + + } + + // 随机 level 次,如果是奇数层数 +1,防止伪随机 + private int randomLevel() { + int level = 1; + for (int i = 1; i < MAX_LEVEL; ++i) { + if (r.nextInt() % 2 == 1) { + level++; + } + } + + return level; + } + + public void printAll() { + Node p = head; + while (p.forwards[0] != null) { + System.out.print(p.forwards[0] + " "); + p = p.forwards[0]; + } + System.out.println(); + } + + public class Node { + private int data = -1; + private Node forwards[] = new Node[MAX_LEVEL]; + private int maxLevel = 0; + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{ data: "); + builder.append(data); + builder.append("; levels: "); + builder.append(maxLevel); + builder.append(" }"); + + return builder.toString(); + } + } + +} diff --git a/java/18_hashtable/HashTable.java b/java/18_hashtable/HashTable.java new file mode 100644 index 00000000..9db7c7f1 --- /dev/null +++ b/java/18_hashtable/HashTable.java @@ -0,0 +1,176 @@ +/** + * @Description:散列表实现 + * @Author: Hoda + * @Date: Create in 2019-08-07 + * @Modified By: + * @Modified Date: + */ +public class HashTable { + + /** + * 散列表默认长度 + */ + private static final int DEFAULT_INITAL_CAPACITY = 8; + + /** + * 装载因子 + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * 初始化散列表数组 + */ + private Entry[] table; + + /** + * 实际元素数量 + */ + private int size = 0; + + /** + * 散列表索引数量 + */ + private int use = 0; + + public HashTable() { + table = (Entry[]) new Entry[DEFAULT_INITAL_CAPACITY]; + } + + static class Entry { + K key; + + V value; + + Entry next; + + Entry(K key, V value, Entry next) { + this.key = key; + this.value = value; + this.next = next; + } + } + + /** + * 新增 + * + * @param key + * @param value + */ + public void put(K key, V value) { + int index = hash(key); + // 位置未被引用,创建哨兵节点 + if (table[index] == null) { + table[index] = new Entry<>(null, null, null); + } + + Entry tmp = table[index]; + // 新增节点 + if (tmp.next == null) { + tmp.next = new Entry<>(key, value, null); + size++; + use++; + // 动态扩容 + if (use >= table.length * LOAD_FACTOR) { + resize(); + } + } + // 解决散列冲突,使用链表法 + else { + do { + tmp = tmp.next; + // key相同,覆盖旧的数据 + if (tmp.key == key) { + tmp.value = value; + return; + } + } while (tmp.next != null); + + Entry temp = table[index].next; + table[index].next = new Entry<>(key, value, temp); + size++; + } + } + + /** + * 散列函数 + *

+ * 参考hashmap散列函数 + * + * @param key + * @return + */ + private int hash(Object key) { + int h; + return (key == null) ? 0 : ((h = key.hashCode()) ^ (h >>> 16)) % table.length; + } + + /** + * 扩容 + */ + private void resize() { + Entry[] oldTable = table; + table = (Entry[]) new Entry[table.length * 2]; + use = 0; + for (int i = 0; i < oldTable.length; i++) { + if (oldTable[i] == null || oldTable[i].next == null) { + continue; + } + Entry e = oldTable[i]; + while (e.next != null) { + e = e.next; + int index = hash(e.key); + if (table[index] == null) { + use++; + // 创建哨兵节点 + table[index] = new Entry<>(null, null, null); + } + table[index].next = new Entry<>(e.key, e.value, table[index].next); + } + } + } + + /** + * 删除 + * + * @param key + */ + public void remove(K key) { + int index = hash(key); + Entry e = table[index]; + if (e == null || e.next == null) { + return; + } + + Entry pre; + do { + pre = e; + e = e.next; + if (key == e.key) { + pre.next = e.next; + size--; + return; + } + } while (e.next != null); + } + + /** + * 获取 + * + * @param key + * @return + */ + public V get(K key) { + int index = hash(key); + Entry e = table[index]; + if (e == null || e.next == null) { + return null; + } + while (e.next != null) { + e = e.next; + if (key == e.key) { + return e.value; + } + } + return null; + } +} diff --git a/java/20_hashtable/LRUBaseHashTable.java b/java/20_hashtable/LRUBaseHashTable.java new file mode 100644 index 00000000..8aeb8eb0 --- /dev/null +++ b/java/20_hashtable/LRUBaseHashTable.java @@ -0,0 +1,195 @@ +import java.util.HashMap; + +/** + * @Description:基于散列表的LRU算法 + * @Author: Hoda + * @Date: Create in 2019-08-09 + * @Modified By: + * @Modified Date: + */ +public class LRUBaseHashTable { + + /** + * 默认链表容量 + */ + private final static Integer DEFAULT_CAPACITY = 10; + + /** + * 头结点 + */ + private DNode headNode; + + /** + * 尾节点 + */ + private DNode tailNode; + + /** + * 链表长度 + */ + private Integer length; + + /** + * 链表容量 + */ + private Integer capacity; + + /** + * 散列表存储key + */ + private HashMap> table; + + /** + * 双向链表 + */ + static class DNode { + + private K key; + + /** + * 数据 + */ + private V value; + + /** + * 前驱指针 + */ + private DNode prev; + + /** + * 后继指针 + */ + private DNode next; + + DNode() { + } + + DNode(K key, V value) { + this.key = key; + this.value = value; + } + + } + + public LRUBaseHashTable(int capacity) { + this.length = 0; + this.capacity = capacity; + + headNode = new DNode<>(); + + tailNode = new DNode<>(); + + headNode.next = tailNode; + tailNode.prev = headNode; + + table = new HashMap<>(); + } + + public LRUBaseHashTable() { + this(DEFAULT_CAPACITY); + } + + /** + * 新增 + * + * @param key + * @param value + */ + public void add(K key, V value) { + DNode node = table.get(key); + if (node == null) { + DNode newNode = new DNode<>(key, value); + table.put(key, newNode); + addNode(newNode); + + if (++length > capacity) { + DNode tail = popTail(); + table.remove(tail.key); + length--; + } + } else { + node.value = value; + moveToHead(node); + } + } + + /** + * 将新节点加到头部 + * + * @param newNode + */ + private void addNode(DNode newNode) { + newNode.next = headNode.next; + newNode.prev = headNode; + + headNode.next.prev = newNode; + headNode.next = newNode; + } + + /** + * 弹出尾部数据节点 + */ + private DNode popTail() { + DNode node = tailNode.prev; + removeNode(node); + return node; + } + + /** + * 移除节点 + * + * @param node + */ + private void removeNode(DNode node) { + node.prev.next = node.next; + node.next.prev = node.prev; + } + + /** + * 将节点移动到头部 + * + * @param node + */ + private void moveToHead(DNode node) { + removeNode(node); + addNode(node); + } + + /** + * 获取节点数据 + * + * @param key + * @return + */ + public V get(K key) { + DNode node = table.get(key); + if (node == null) { + return null; + } + moveToHead(node); + return node.value; + } + + /** + * 移除节点数据 + * + * @param key + */ + public void remove(K key) { + DNode node = table.get(key); + if (node == null) { + return; + } + removeNode(node); + length--; + } + + private void printAll() { + DNode node = headNode.next; + while (node.next != null) { + System.out.print(node.value + ","); + node = node.next; + } + System.out.println(); + } +} \ No newline at end of file diff --git a/java/24_tree/BinarySearchTree.java b/java/24_tree/BinarySearchTree.java new file mode 100644 index 00000000..69850d11 --- /dev/null +++ b/java/24_tree/BinarySearchTree.java @@ -0,0 +1,99 @@ +public class BinarySearchTree { + private Node tree; + + public Node find(int data) { + Node p = tree; + while (p != null) { + if (data < p.data) p = p.left; + else if (data > p.data) p = p.right; + else return p; + } + return null; + } + + public void insert(int data) { + if (tree == null) { + tree = new Node(data); + return; + } + + Node p = tree; + while (p != null) { + if (data > p.data) { + if (p.right == null) { + p.right = new Node(data); + return; + } + p = p.right; + } else { // data < p.data + if (p.left == null) { + p.left = new Node(data); + return; + } + p = p.left; + } + } + } + + public void delete(int data) { + Node p = tree; // p指向要删除的节点,初始化指向根节点 + Node pp = null; // pp记录的是p的父节点 + while (p != null && p.data != data) { + pp = p; + if (data > p.data) p = p.right; + else p = p.left; + } + if (p == null) return; // 没有找到 + + // 要删除的节点有两个子节点 + if (p.left != null && p.right != null) { // 查找右子树中最小节点 + Node minP = p.right; + Node minPP = p; // minPP表示minP的父节点 + while (minP.left != null) { + minPP = minP; + minP = minP.left; + } + p.data = minP.data; // 将minP的数据替换到p中 + p = minP; // 下面就变成了删除minP了 + pp = minPP; + } + + // 删除节点是叶子节点或者仅有一个子节点 + Node child; // p的子节点 + if (p.left != null) child = p.left; + else if (p.right != null) child = p.right; + else child = null; + + if (pp == null) tree = child; // 删除的是根节点 + else if (pp.left == p) pp.left = child; + else pp.right = child; + } + + public Node findMin() { + if (tree == null) return null; + Node p = tree; + while (p.left != null) { + p = p.left; + } + return p; + } + + public Node findMax() { + if (tree == null) return null; + Node p = tree; + while (p.right != null) { + p = p.right; + } + return p; + } + + public static class Node { + private int data; + private Node left; + private Node right; + + public Node(int data) { + this.data = data; + } + } +} diff --git a/java/28_sorts/HeapSort.java b/java/28_sorts/HeapSort.java new file mode 100644 index 00000000..27840493 --- /dev/null +++ b/java/28_sorts/HeapSort.java @@ -0,0 +1,74 @@ +/** + * 堆排序 + */ +public class HeapSort { + + /** + * 排序 + *

+ * 堆元素是从数组下标0开始 + * + * @param arr + */ + public static void sort(int[] arr) { + if (arr.length <= 1) { + return; + } + + // 1、建堆 + buildHeap(arr); + + // 2、排序 + int k = arr.length - 1; + while (k > 0) { + // 将堆顶元素(最大)与最后一个元素交换位置 + swap(arr, 0, k); + // 将剩下元素重新堆化,堆顶元素变成最大元素 + heapify(arr, --k, 0); + } + } + + /** + * 建堆 + * + * @param arr + */ + private static void buildHeap(int[] arr) { + // (arr.length - 1) / 2 为最后一个叶子节点的父节点 + // 也就是最后一个非叶子节点,依次堆化直到根节点 + for (int i = (arr.length - 1) / 2; i >= 0; i--) { + heapify(arr, arr.length - 1, i); + } + } + + /** + * 堆化 + * + * @param arr 要堆化的数组 + * @param n 最后堆元素下标 + * @param i 要堆化的元素下标 + */ + private static void heapify(int[] arr, int n, int i) { + while (true) { + // 最大值位置 + int maxPos = i; + // 与左子节点(i * 2 + 1)比较,获取最大值位置 + if (i * 2 + 1 <= n && arr[i] < arr[i * 2 + 1]) { + maxPos = i * 2 + 1; + } + // 最大值与右子节点(i * 2 + 2)比较,获取最大值位置 + if (i * 2 + 2 <= n && arr[maxPos] < arr[i * 2 + 2]) { + maxPos = i * 2 + 2; + } + // 最大值是当前位置结束循环 + if (maxPos == i) { + break; + } + // 与子节点交换位置 + swap(arr, i, maxPos); + // 以交换后子节点位置接着往下查找 + i = maxPos; + } + } + +} \ No newline at end of file diff --git a/java/32_BFRK b/java/32_BFRK new file mode 100644 index 00000000..b79ee583 --- /dev/null +++ b/java/32_BFRK @@ -0,0 +1,49 @@ +public static int bF(String a,String b) { + int m=a.length(),n=b.length(),k; + char[] a1=a.toCharArray(); + char[] b1=b.toCharArray(); + for(int i=0;i<=m-n;i++) { + k=0; + for(int j=0;j queue = new LinkedList<>(); + queue.add(root); + + while (!queue.isEmpty()) { + ACNode p = queue.pop(); + + for(ACNode pc: p.children.values()){ + if (Objects.isNull(pc)) { + continue; + } + + if(p == root) { + pc.fail = root; + } else { + ACNode q = p.fail; + while (Objects.nonNull(q)) { + ACNode qc = q.children.get(pc.data); + if(Objects.nonNull(qc)) { + pc.fail = qc; + break; + } + q = q.fail; + } + if(Objects.isNull(q)) { + pc.fail = root; + } + } + queue.add(pc); + } + } + } + + private Boolean match (String text) { + ACNode root = this.root; + ACNode p = root; + + int n = text.length(); + for(int i = 0; i < n; i++) { + String c = text.charAt(i) + ""; + while(Objects.isNull(p.children.get(c)) && p != root){ + p = p.fail; + } + + p = p.children.get(c); + if(Objects.isNull(p)) { + p = root; + } + + ACNode tmp = p; + while ( tmp != root) { + if (tmp.isEndingChar == true) { + System.out.println("Start from " + (i - p.length + 1)); + return true; + } + tmp = tmp.fail; + } + } + + return false; + } + + public static boolean match(String text, String[] patterns) { + ACAutoMata automata = new ACAutoMata(); + for (String pattern: patterns) { + automata.insert(pattern); + } + + automata.buildFailurePointer(); + return automata.match(text); + } + + public class ACNode { + private String data; + private Map children; + private Boolean isEndingChar; + private Integer length; + private ACNode fail; + + public ACNode(String data) { + this.data = data; + this.children = new HashMap<>(); + this.isEndingChar = false; + this.length = 0; + this.fail = null; + } + } + + public static void main(String[] args) { + String[] patterns = {"at", "art", "oars", "soar"}; + String text = "soarsoars"; + System.out.println(match(text, patterns)); + + String[] patterns2 = {"Fxtec Pro1", "谷歌Pixel"}; + + String text2 = "一家总部位于伦敦的公司Fxtex在MWC上就推出了一款名为Fxtec Pro1的手机,该机最大的亮点就是采用了侧滑式全键盘设计。DxOMark年度总榜发布 华为P20 Pro/谷歌Pixel 3争冠"; + System.out.println(match(text2, patterns2)); + } +} diff --git a/javascript/05_array/Array.md b/javascript/05_array/Array.md index 0b0cc305..d2784d1e 100644 --- a/javascript/05_array/Array.md +++ b/javascript/05_array/Array.md @@ -151,7 +151,7 @@ function isEven(num){ } var num = [1,2,3,4,5,6,7,8]; var someEven = num.some(isEven); -if(even){ +if(someEven){ console.log("有些数字是偶数"); }else{ console.log("没有数字是偶数"); @@ -208,14 +208,24 @@ console.log(pass); #### 二维数组 JavaScript 可以通过在数组里在嵌套一个数组来形成二维数组。 ``` -var grades = [[88,86,82],[91,82,83],[77,72,79]]; +var grades = [ + [88,86,82], + [91,82,83], + [77,72,79], + [86,80,82] +]; console.log(grades[1][2]); // 83 ``` #### 处理二维数组 对于二维数组的处理可以分为两种,一种按列访问,一种是按行访问。 -按列访问,外层循环对应行,内层循环对应列。例如,上述的数组,每一行对应一个学生的成绩记录,可以通过相加所有成绩,然后除以科目数来得到该生的平均成绩。 -``` -var grades = [[88,86,82],[91,82,83],[77,72,79]]; +按列访问,外层循环对应行,内层循环对应列。例如,上述的数组,每一行对应一个学生三门科目的成绩记录,可以通过相加所有成绩,然后除以科目数来得到该生的平均成绩。 +``` +var grades = [ + [88,86,82], + [91,82,83], + [77,72,79], + [86,80,82] +]; var total = 0; var average = 0.0; for(var row = 0;row student 1 average: 85.33 student 2 average: 85.33 student 3 average: 76.00 +student 4 average: 82.67 -对于按行访问,则外层循环对应列,内城循环对应行,例如还是上述数组,现在的数组表示一个学生各科的分数,我们来求其平均成绩 -``` -var grades = [[88,86,82],[91,82,83],[77,72,79]]; +对于按行访问,则外层循环对应列,内层循环对应行,例如还是上述数组,现在的数组表示一个学生三场考试四门科目的各科分数,我们来求每场考试的平均成绩 +``` +var grades = [ + [88,86,82], + [91,82,83], + [77,72,79], + [86,80,82] +]; var total = 0; var average = 0.0; -for(var col = 0;col exam 1 average: 85.33 +> exam 1 average: 85.50 exam 2 average: 80.00 -exam 3 average: 81.33 +exam 3 average: 81.50 其实只要调整 for 循环的顺序就可以控制是按行还是按列来输出,此外,JavaScript 还可以处理一些参差不齐的数组,比如一个二维数组中的数组,有的是两个元素,有的是四个元素,并不是都相同,在这种情况下,JavaScript 依然可以处理运行而不报错,这是因为不管多或少,都可以通过 length 属性来计算。 #### 对象数组 @@ -282,7 +299,7 @@ Point 2: 2, 4 Point 3: 8, 1 Point 4: 2, 9 -也可以用之前的 puh() 等操作方法来操作对象数组 +也可以用之前的 push() 等操作方法来操作对象数组 ``` var p5 = new Point(11,13); point.push(p5); diff --git a/javascript/06_linkedlist/SinglyLinkedList.js b/javascript/06_linkedlist/SinglyLinkedList.js index 2b079208..3e49d148 100644 --- a/javascript/06_linkedlist/SinglyLinkedList.js +++ b/javascript/06_linkedlist/SinglyLinkedList.js @@ -15,7 +15,7 @@ class LinkedList { } // 根据value查找节点 findByValue (item) { - let currentNode = this.head + let currentNode = this.head.next while (currentNode !== null && currentNode.element !== item) { currentNode = currentNode.next } @@ -23,9 +23,9 @@ class LinkedList { return currentNode === null ? -1 : currentNode } - // 根据index查找节点 + // 根据index查找节点,下标从0开始 findByIndex (index) { - let currentNode = this.head + let currentNode = this.head.next let pos = 0 while (currentNode !== null && pos !== index) { currentNode = currentNode.next @@ -33,7 +33,17 @@ class LinkedList { } console.log(currentNode) return currentNode === null ? -1 : currentNode - } + } + + // 向链表末尾追加节点 + append(newElement) { + const newNode = new Node(newElement) + let currentNode = this.head + while(currentNode.next) { + currentNode = currentNode.next + } + currentNode.next = newNode + } // 指定元素向后插入 insert (newElement, element) { @@ -61,18 +71,17 @@ class LinkedList { // 根据值删除 remove (item) { - const desNode = this.findByValue(item) - if (desNode === -1) { + const prevNode = this.findPrev(item) + if (prevNode === -1) { console.log('未找到元素') return } - const prevNode = this.findPrev(item) - prevNode.next = desNode.next - } + prevNode.next = prevNode.next.next + } // 遍历显示所有节点 display () { - let currentNode = this.head + let currentNode = this.head.next // 忽略头指针的值 while (currentNode !== null) { console.log(currentNode.element) currentNode = currentNode.next @@ -81,14 +90,24 @@ class LinkedList { } // Test const LList = new LinkedList() -LList.insert('chen', 'head') -LList.insert('curry', 'chen') -LList.insert('sang', 'head') -LList.insert('zhao', 'head') +LList.append('chen') +LList.append('curry') +LList.append('sang') +LList.append('zhao') // chen -> curry -> sang -> zhao +console.log('-------------insert item------------') +LList.insert('qian', 'chen') // 首元素后插入 +LList.insert('zhou', 'zhao') // 尾元素后插入 +LList.display() // chen -> qian -> curry -> sang -> zhao -> zhou console.log('-------------remove item------------') -LList.remove('curry', 'chen') -LList.display() +LList.remove('curry') +LList.display() // chen -> qian -> sang -> zhao -> zhou console.log('-------------find by item------------') LList.findByValue('chen') console.log('-------------find by index------------') LList.findByIndex(2) +console.log('-------------与头结点同值元素测试------------') +LList.insert('head', 'sang') +LList.display() // chen -> qian -> sang -> head -> zhao -> zhou +LList.findPrev('head') // sang +LList.remove('head') +LList.display() // chen -> qian -> sang -> zhao -> zhou diff --git a/javascript/07_linkedlist/LinkedListAlgo.js b/javascript/07_linkedlist/LinkedListAlgo.js index 39cf6e49..77695c99 100644 --- a/javascript/07_linkedlist/LinkedListAlgo.js +++ b/javascript/07_linkedlist/LinkedListAlgo.js @@ -33,7 +33,7 @@ class LinkedList { currentNode = currentNode.next pos++ } - return currentNode === null ? -1 : pos + return currentNode === null ? -1 : currentNode } // 指定元素向后插入 insert(newElement, element) { @@ -69,6 +69,9 @@ class LinkedList { } // 遍历显示所有节点 display() { + //先检查是否为环 + if(this.checkCircle()) return false + let currentNode = this.head while (currentNode !== null) { console.log(currentNode.element) @@ -89,6 +92,30 @@ class LinkedList { this.head = root } + //增强尾插法可读性,便于初学者理解 + reverseList1(){ + //head节点即哨兵,作用就是使所有链表, + // 包括空链表的头节点不为null,并使对单链表的插入、删除操作不需要区分是否为空表或是否在第一个位置进行, + // 从而与其他位置的插入、删除操作一致 + //所以反转链表的时候不需要带上head节点 + let currentNode=this.head.next + //第一个节点头结点让其指向null + let previousNode=null + while(currentNode!==null){ + //务必先保留下一节点的指针地址 + let nextNode=currentNode.next + //第一次是null + currentNode.next=previousNode + //此时将previousNode赋值为当前节点, + // 那么下次循环的时候,方便下次的currentNode指向previousNode + previousNode=currentNode + //抬走,下一个! + currentNode=nextNode + } + //最后将反转好的链表加上头节点 + this.head.next=previousNode + } + // 自己一开始瞎想的。差距啊 reverseList2() { let currentNode = this.head.next @@ -122,6 +149,8 @@ class LinkedList { } // 删除倒数第k个节点 removeByIndexFromEnd(index) { + //务必先判断是否是 环链表 + if(this.checkCircle()) return false let pos = 1 this.reverseList() let currentNode = this.head.next diff --git a/javascript/08_stack/SampleBrowser.js b/javascript/08_stack/SampleBrowser.js new file mode 100644 index 00000000..c084dd78 --- /dev/null +++ b/javascript/08_stack/SampleBrowser.js @@ -0,0 +1,56 @@ +/** + * 使用前后栈实现浏览器的前进后退。 + * + * Author nameczz + */ +const stack = require('./StackBasedOnLinkedList') + +class SampleBrowser { + constructor() { + this.normalStack = new stack.CreatedStack() + this.backStack = new stack.CreatedStack() + } + // 正常浏览页面 + pushNormal(name) { + this.normalStack.push(name) + this.backStack.clear() + this.displayAllStack() + } + // 后退 + back() { + const value = this.normalStack.pop() + if (value !== -1) { + this.backStack.push(value) + this.displayAllStack() + } else { + console.log('无法后退') + } + } + // 前进 + front() { + const value = this.backStack.pop() + if (value !== -1) { + this.normalStack.push(value) + this.displayAllStack() + } else { + console.log('无法前进') + } + } + // 打印栈内数据 + displayAllStack() { + console.log('---后退页面---') + this.backStack.display() + console.log('---浏览页面---') + this.normalStack.display() + } +} +// Test +const browser = new SampleBrowser() +browser.pushNormal('www.google.com') +browser.pushNormal('www.baidu.com') +browser.pushNormal('www.github.com') +// 后退 +browser.back() +browser.back() +browser.front() +browser.pushNormal('www.new.com') \ No newline at end of file diff --git a/javascript/08_stack/StackBasedOnLinkedList.js b/javascript/08_stack/StackBasedOnLinkedList.js new file mode 100644 index 00000000..07acc0e6 --- /dev/null +++ b/javascript/08_stack/StackBasedOnLinkedList.js @@ -0,0 +1,62 @@ +/** + * 基于链表实现的栈。 + * + * Author: nameczz + */ + +class Node { + constructor(element) { + this.element = element + this.next = null + } +} + +class StackBasedLinkedList { + constructor() { + this.top = null + } + push(value) { + const node = new Node(value) + if (this.top === null) { + this.top = node + } else { + node.next = this.top + this.top = node + } + } + pop() { + if (this.top === null) { + return -1 + } + const value = this.top.element + this.top = this.top.next + return value + } + // 为了实现浏览器前进后退 + clear() { + this.top = null + } + display() { + if (this.top !== null) { + let temp = this.top + while (temp !== null) { + console.log(temp.element) + temp = temp.next + } + } + } +} +// Test +const newStack = new StackBasedLinkedList() +newStack.push(1) +newStack.push(2) +newStack.push(3) +// 获取元素 +let res = 0 +console.log('-------获取pop元素------') +while (res !== -1) { + res = newStack.pop() + console.log(res) +} + +exports.CreatedStack = StackBasedLinkedList \ No newline at end of file diff --git a/javascript/09_queue/CircularQueueBasedOnLinkedList.js b/javascript/09_queue/CircularQueueBasedOnLinkedList.js new file mode 100644 index 00000000..7dac61dc --- /dev/null +++ b/javascript/09_queue/CircularQueueBasedOnLinkedList.js @@ -0,0 +1,71 @@ +/** + * 基于链表实现的循环队列。 + * + * Author: nameczz + */ + +class Node { + constructor(element) { + this.element = element + this.next = null + } +} + +class CircularQueue { + constructor() { + this.head = null + this.tail = null + } + + enqueue(value) { + if (this.head === null) { + this.head = new Node(value) + this.head.next = this.head + this.tail = this.head + } else { + const flag = this.head === this.tail + this.tail.next = new Node(value) + this.tail.next.next = this.head + this.tail = this.tail.next + if (flag) { + this.head.next = this.tail + } + } + } + + dequeue() { + if(this.head == null) return -1 + + if (this.head === this.tail) { + const value = this.head.element + this.head = null + return value + } else { + const value = this.head.element + this.head = this.head.next + this.tail.next = this.head + return value + } + } + + display() { + let res = 0 + console.log('-------获取dequeue元素------') + while (res !== -1) { + res = this.dequeue() + console.log(res) + } + } +} +// Test +const newCircularQueue = new CircularQueue() +// 插入元素 +newCircularQueue.enqueue(1) +newCircularQueue.enqueue(2) +newCircularQueue.enqueue(3) +// 获取元素 +newCircularQueue.display() +newCircularQueue.enqueue(1) +newCircularQueue.display() + +// exports.CreatedStack = StackBasedLinkedList \ No newline at end of file diff --git a/javascript/09_queue/QueueBasedOnLinkedList.js b/javascript/09_queue/QueueBasedOnLinkedList.js new file mode 100644 index 00000000..75918585 --- /dev/null +++ b/javascript/09_queue/QueueBasedOnLinkedList.js @@ -0,0 +1,52 @@ +/** + * 基于链表实现的队列。 + * + * Author: nameczz + */ + +class Node { + constructor(element) { + this.element = element + this.next = null + } +} + +class QueueBasedOnLinkedList { + constructor() { + this.head = null + this.tail = null + } + + enqueue(value) { + if (this.head === null) { + this.head = new Node(value) + this.tail = this.head + } else { + this.tail.next = new Node(value) + this.tail = this.tail.next + } + } + + dequeue() { + if (this.head !== null) { + const value = this.head.element + this.head = this.head.next + return value + } else { + return -1 + } + } +} +// Test +const newQueue = new QueueBasedOnLinkedList() +// 插入元素 +newQueue.enqueue(1) +newQueue.enqueue(2) +newQueue.enqueue(3) +// 获取元素 +let res = 0 +console.log('-------获取dequeue元素------') +while (res !== -1) { + res = newQueue.dequeue() + console.log(res) +} diff --git a/javascript/11_sorts/sort.js b/javascript/11_sorts/sort.js new file mode 100644 index 00000000..0a3c9c8f --- /dev/null +++ b/javascript/11_sorts/sort.js @@ -0,0 +1,68 @@ +/** + * 冒泡,插入,选择排序 + * + * Author: nameczz + */ + +// 冒泡排序 +const bubbleSort = (arr) => { + if (arr.length <= 1) return + for (let i = 0; i < arr.length; i++) { + let hasChange = false + for (let j = 0; j < arr.length - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + const temp = arr[j] + arr[j] = arr[j + 1] + arr[j + 1] = temp + hasChange = true + } + } + // 如果false 说明所有元素已经到位 + if (!hasChange) break + } + console.log(arr) +} + +// 插入排序 +const insertionSort = (arr) => { + if (arr.length <= 1) return + for (let i = 1; i < arr.length; i++) { + const temp = arr[i] + let j = i - 1 + // 若arr[i]前有大于arr[i]的值的化,向后移位,腾出空间,直到一个<=arr[i]的值 + for (j; j >= 0; j--) { + if (arr[j] > temp) { + arr[j + 1] = arr[j] + } else { + break + } + } + arr[j + 1] = temp + } + console.log(arr) +} + +// 选择排序 +const selectionSort = (arr) => { + if (arr.length <= 1) return + // 需要注意这里的边界, 因为需要在内层进行 i+1后的循环,所以外层需要 数组长度-1 + for (let i = 0; i < arr.length - 1; i++) { + let minIndex = i + for (let j = i + 1; j < arr.length; j++) { + if (arr[j] < arr[minIndex]) { + minIndex = j // 找到整个数组的最小值 + } + } + const temp = arr[i] + arr[i] = arr[minIndex] + arr[minIndex] = temp + } + console.log(arr) +} + +const test = [4, 5, 6, 3, 2, 1] +bubbleSort(test) +const testSort = [4, 1, 6, 3, 2, 1] +insertionSort(testSort) +const testSelect = [4, 8, 6, 3, 2, 1, 0, 12] +selectionSort(testSelect) \ No newline at end of file diff --git a/javascript/12_sorts/KthNum.js b/javascript/12_sorts/KthNum.js new file mode 100644 index 00000000..328b62c2 --- /dev/null +++ b/javascript/12_sorts/KthNum.js @@ -0,0 +1,40 @@ +/** + * 第k大的数 + * @param {array} arr + * @param {number} k + */ +function kthNum(arr, k) { + const len = arr.length; + if (k > len) { + return -1; + } + let p = partition(arr, 0, len - 1); + while (p + 1 !== k) { + if (p + 1 > k) { + p = partition(arr, 0, p - 1); + } else { + p = partition(arr, p + 1, len - 1); + } + } + return arr[p]; +} + +function partition(arr, start, end) { + let i = start; + let pivot = arr[end]; + for (let j = start; j < end; j++) { + if (arr[j] < pivot) { + swap(arr, i, j); + i += 1; + } + } + swap(arr, i, end); + return i; +} + +function swap(arr, i, j) { + if (i === j) return; + let tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; +} diff --git a/javascript/12_sorts/MergeSort.js b/javascript/12_sorts/MergeSort.js new file mode 100644 index 00000000..6185ab46 --- /dev/null +++ b/javascript/12_sorts/MergeSort.js @@ -0,0 +1,43 @@ +/** + * 归并排序 + * + * Author: nameczz + */ + +const mergeArr = (left, right) => { + let temp = [] + let leftIndex = 0 + let rightIndex = 0 + // 判断2个数组中元素大小,依次插入数组 + while (left.length > leftIndex && right.length > rightIndex) { + if (left[leftIndex] <= right[rightIndex]) { + temp.push(left[leftIndex]) + leftIndex++ + } else { + temp.push(right[rightIndex]) + rightIndex++ + } + } + // 合并 多余数组 + return temp.concat(left.slice(leftIndex)).concat(right.slice(rightIndex)) +} + +const mergeSort = (arr) => { + // 当任意数组分解到只有一个时返回。 + if (arr.length <= 1) return arr + const middle = Math.floor(arr.length / 2) // 找到中间值 + const left = arr.slice(0, middle) // 分割数组 + const right = arr.slice(middle) + // 递归 分解 合并 + return mergeArr(mergeSort(left), mergeSort(right)) +} + +const testArr = [] +let i = 0 +while (i < 100) { + testArr.push(Math.floor(Math.random() * 1000)) + i++ +} + +const res = mergeSort(testArr) +console.log(res) \ No newline at end of file diff --git a/javascript/12_sorts/QuickSort.js b/javascript/12_sorts/QuickSort.js new file mode 100644 index 00000000..d8ba3bdc --- /dev/null +++ b/javascript/12_sorts/QuickSort.js @@ -0,0 +1,46 @@ +/** + * 快速排序 + * + * Author: nameczz + */ + +const swap = (arr, i, j) => { + const temp = arr[i] + arr[i] = arr[j] + arr[j] = temp +} + +// 获取 pivot 交换完后的index +const partition = (arr, pivot, left, right) => { + const pivotVal = arr[pivot] + let startIndex = left + for (let i = left; i < right; i++) { + if (arr[i] < pivotVal) { + swap(arr, i, startIndex) + startIndex++ + } + } + swap(arr, startIndex, pivot) + return startIndex +} + +const quickSort = (arr, left, right) => { + if (left < right) { + let pivot = right + let partitionIndex = partition(arr, pivot, left, right) + quickSort(arr, left, partitionIndex - 1 < left ? left : partitionIndex - 1) + quickSort(arr, partitionIndex + 1 > right ? right : partitionIndex + 1, right) + } + +} + + +const testArr = [] +let i = 0 +while (i < 10) { + testArr.push(Math.floor(Math.random() * 1000)) + i++ +} +console.log('unsort', testArr) +quickSort(testArr, 0, testArr.length - 1); +console.log('sort', testArr) \ No newline at end of file diff --git a/javascript/15_binary/binaryFind.js b/javascript/15_binary/binaryFind.js new file mode 100644 index 00000000..86ae4ef9 --- /dev/null +++ b/javascript/15_binary/binaryFind.js @@ -0,0 +1,27 @@ +/** + * 二分查找 + * + * Author: nameczz + */ +// 数组必须有序 不存在重复 +const biaryFind = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target === sortedArr[mid]) { + return mid + } else if (target < sortedArr[mid]) { + high = mid - 1 + } else { + low = mid + 1 + } + } + return -1 +} +const arr = [1, 4, 5, 6, 7, 8, 10, 11, 23, 42, 44, 54, 56, 77, 102] +console.log(biaryFind(arr, 44)) +console.log(biaryFind(arr, 1)) +console.log(biaryFind(arr, 102)) +console.log(biaryFind(arr, 1111)) \ No newline at end of file diff --git a/javascript/16_binary/binary-find.js b/javascript/16_binary/binary-find.js new file mode 100644 index 00000000..9cc51c26 --- /dev/null +++ b/javascript/16_binary/binary-find.js @@ -0,0 +1,90 @@ +/** + * 二分查找 + * + * Author: nameczz + */ + + +// 查找第一个等于给定值 +const biaryFindFirst = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + + if (target < sortedArr[mid]) { + high = mid - 1 + } else if (target > sortedArr[mid]) { + low = mid + 1 + } else { + if (mid === 0 || sortedArr[mid - 1] < target) return mid + high = mid - 1 + } + } + return -1 +} + +// 查找最后一个相等的数 +const biaryFindLast = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target < sortedArr[mid]) { + high = mid - 1 + } else if (target > sortedArr[mid]) { + low = mid + 1 + } else { + if (mid === sortedArr.length - 1 || sortedArr[mid + 1] > target) return mid + low = mid + 1 + } + } + return -1 +} + +// 查找第一个大于等于给定值的元素 +const biaryFindFistBig = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target <= sortedArr[mid]) { + if (mid === 0 || sortedArr[mid - 1] < target) return mid + high = mid - 1 + } else { + low = mid + 1 + } + } + return -1 +} + +// 查找最后一个小于等于给定值的元素 +const biaryFindLastSmall = (sortedArr, target) => { + if (sortedArr.length === 0) return -1 + let low = 0 + let high = sortedArr.length - 1 + while (low <= high) { + const mid = Math.floor((low + high) / 2) + if (target < sortedArr[mid]) { + high = mid - 1 + } else { + if (mid === sortedArr.length - 1 || sortedArr[mid + 1] >= target) return mid + low = mid + 1 + } + } + return -1 +} + +const arr = [1, 2, 3, 4, 4, 4, 4, 4, 6, 7, 8, 8, 9] +const first = biaryFindFirst(arr, 4) +console.log(`FindFirst: ${first}`) + +const last = biaryFindLast(arr, 4) +console.log(`FindLast: ${last}`) +const FisrtBig = biaryFindFistBig(arr, 5) +console.log(`FindFisrtBig: ${FisrtBig}`) +const LastSmall = biaryFindLastSmall(arr, 4) +console.log(`FindLastSmall: ${LastSmall}`) diff --git a/javascript/17_skiplist/SkipList.js b/javascript/17_skiplist/SkipList.js new file mode 100644 index 00000000..1b9bd782 --- /dev/null +++ b/javascript/17_skiplist/SkipList.js @@ -0,0 +1,133 @@ +/** + * author dreamapplehappy + * 关于代码的一些解释可以看一下这里:https://github.com/dreamapplehappy/blog/tree/master/2018/12/02 + */ + +const MAX_LEVEL = 16; + +class Node { + constructor({ + data = -1, + maxLevel = 0, + refer = new Array(MAX_LEVEL) + } = {}) { + this.data = data; + this.maxLevel = maxLevel; + this.refer = refer + } +} + +class SkipList{ + constructor() { + this.head = new Node(); + this.levelCount = 1; + } + randomLevel() { + let level = 1; + for (let i = 1; i < MAX_LEVEL; i++) { + if (Math.random() < 0.5) { + level++; + } + } + return level; + } + insert(value) { + const level = this.randomLevel(); + const newNode = new Node(); + newNode.data = value; + newNode.maxLevel = level; + const update = new Array(level).fill(new Node()); + let p = this.head; + for(let i = level - 1; i >= 0; i--) { + while(p.refer[i] !== undefined && p.refer[i].data < value) { + p = p.refer[i]; + } + update[i] = p; + } + for(let i = 0; i < level; i++) { + newNode.refer[i] = update[i].refer[i]; + update[i].refer[i] = newNode; + } + if(this.levelCount < level) { + this.levelCount = level; + } + } + + find(value) { + if(!value){return null} + let p = this.head; + for(let i = this.levelCount - 1; i >= 0; i--) { + while(p.refer[i] !== undefined && p.refer[i].data < value) { + p = p.refer[i]; + } + } + + if(p.refer[0] !== undefined && p.refer[0].data === value) { + return p.refer[0]; + } + return null; + } + + remove(value) { + let _node; + let p = this.head; + const update = new Array(new Node()); + for(let i = this.levelCount - 1; i >= 0; i--) { + while(p.refer[i] !== undefined && p.refer[i].data < value){ + p = p.refer[i]; + } + update[i] = p; + } + + if(p.refer[0] !== undefined && p.refer[0].data === value) { + _node = p.refer[0]; + for(let i = 0; i <= this.levelCount - 1; i++) { + if(update[i].refer[i] !== undefined && update[i].refer[i].data === value) { + update[i].refer[i] = update[i].refer[i].refer[i]; + } + } + return _node; + } + return null; + } + + printAll() { + let p = this.head; + while(p.refer[0] !== undefined) { + console.log(p.refer[0].data) + p = p.refer[0]; + } + } +} + +test(); +function test() { + let list = new SkipList(); + let length = 20000; + //顺序插入 + for (let i = 1; i <= 10; i++) { + list.insert(i); + } + //输出一次 + list.printAll(); + console.time('create length-10') + //插入剩下的 + for (let i = 11; i <= length - 10; i++) { + list.insert(i); + } + console.timeEnd('create length-10') + //搜索 10次 + for (let j = 0; j < 10; j++) { + let key = Math.floor(Math.random() * length + 1); + console.log(key, list.find(key)) + } + //搜索不存在的值 + console.log('null:', list.find(length + 1)); + //搜索5000次统计时间 + console.time('search 5000'); + for (let j = 0; j < 5000; j++) { + let key = Math.floor(Math.random() * length + 1); + } + console.timeEnd('search 5000'); +} + diff --git a/javascript/18_hashmap/HashTable.html b/javascript/18_hashmap/HashTable.html new file mode 100644 index 00000000..de046069 --- /dev/null +++ b/javascript/18_hashmap/HashTable.html @@ -0,0 +1,61 @@ + + + + + Title + + + + + diff --git a/javascript/19_hashTable/hashtable.js b/javascript/19_hashTable/hashtable.js new file mode 100644 index 00000000..35abb616 --- /dev/null +++ b/javascript/19_hashTable/hashtable.js @@ -0,0 +1,340 @@ +/**** + * 带碰撞处理的Hash表 + * 实际上在js中,单独实现一个Hash表感觉不是很有实用价值 + * 如果需要通常是直接将Object,Map,Set来当Hash表用 + * + * 总结: + * 我写的这个实现把store 从Object换成Array不会有运行性能上的区别 + * 把hash函数改成生成一定范围的值的类型,然后初始化一个指定长度的数组因该会有一定的性能提升 + * 把store换成Map,然后修改相关实现会获得飞越性的提升,因为在js中Map的实现对这种类型的操作做了优化 + */ +class HashTable { + constructor() { + //创建一个没有原型链的对象 + this.store = Object.create(null); + } + /** + * Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。 + * @param {*} string + * 翻译自别的语言的实现 + * 需要注意的是由于js中没有int类型,number是dobule的标准实现 + * 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同 + */ + hash(string) { + let len = string.length; + let hash = len; + for (let i = 0; i < len; i++) { + hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i); + } + return hash & 0x7FFFFFFF; + } + + isCresh(item) { + return Object.prototype.toString.call(item) === "[object Map]" + } + /** + * 约定item必须要有key + * @param {*} item + */ + put(item) { + if (typeof item.key !== 'string') { + throw 'item must have key!' + } + let hash = this.hash(item.key); + //碰撞处理 + let cresh = this.store[hash]; + if (cresh) { + if (cresh.key === item.key) { + this.store[hash] = item; + return + } + + if (!this.isCresh(cresh)) { + this.store[hash] = new Map(); + } + this.store[hash].set(item.key, item); + } else { + this.store[hash] = item; + } + } + get(key) { + let hash = this.hash(key); + let value = this.store[hash] || null; + if (this.isCresh(value)) { + return value.get(key); + } else { + return value + } + } + remove(key) { + let hash = this.hash(key); + let value = this.store[hash]; + if (!value) { + return null; + } + if (this.isCresh(value)) { + value.delete(key); + } else { + delete this.store[hash]; + } + } + clear() { + this.store = {}; + } + print() { + let values = Object.values(this.store); + values.forEach(element => { + if (this.isCresh(element)) { + element.forEach(item => { + console.log(item); + }); + } else { + console.log(element) + } + }); + } +} +/** + * 相比使用Object和Array做store 运行时的性能提升了三分之一 + * 但当前这种用法没有直接使用Map方便,而且直接使用Map会快的多 + */ +class HashTableBaseMap { + constructor() { + this.store = new Map(); + } + /** + * Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。 + * @param {*} string + * 翻译自别的语言的实现 + * 需要注意的是由于js中没有int类型,number是dobule的标准实现 + * 所以返回前的位运算实际和本来的设想不一致,也就是同样的实现,在别的语言中返回可能不同 + */ + hash(string) { + let len = string.length; + let hash = len; + for (let i = 0; i < len; i++) { + hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i); + } + return hash & 0x7FFFFFFF; + } + + isCresh(item) { + return Object.prototype.toString.call(item) === "[object Map]" + } + /** + * 约定item必须要有key + * @param {*} item + */ + put(item) { + if (typeof item.key !== 'string') { + throw 'item must have key!' + } + let hash = this.hash(item.key); + //碰撞处理 + let cresh = this.store.get(hash); + if (cresh) { + if (cresh.key === item.key) { + this.store.set(hash, item); + return + } + + if (!this.isCresh(cresh)) { + this.store[hash] = new Map(); + } + this.store[hash].set(item.key, item); + } else { + this.store.set(hash, item); + } + } + get(key) { + let hash = this.hash(key); + let value = this.store.get(hash); + if (this.isCresh(value)) { + return value.get(key); + } else { + return value + } + } + remove(key) { + let hash = this.hash(key); + let value = this.store.get(hash); + if (!value) { + return null; + } + if (this.isCresh(value)) { + value.delete(key); + } else { + this.store.delete(hash) + } + } + clear() { + this.store = {}; + } + print() { + this.store.forEach(element => { + if (this.isCresh(element)) { + element.forEach(item => { + console.log(item); + }); + } else { + console.log(element) + } + }); + } +} + +/** + * 基础测试 + */ +function baseTest() { + let hashTable = new HashTable(); + for (let i = 0; i < 10; i++) { + hashTable.put({ + key: 'test' + i, + value: 'some value' + i + }); + } + console.log('step1:') + //随机获取5次 + for (let j = 0; j < 5; j++) { + let key = 'test' + Math.floor(Math.random() * 10); + console.log(key); + console.log(hashTable.get(key)) + } + //获得一次空值 + console.log('get null:', hashTable.get('test10')) + //修改一次值 + hashTable.put({ + key: 'test1', + value: 'change' + }); + //删除一次值 + hashTable.remove('test2'); + console.log('step2:') + //输出修改后所有的 + hashTable.print(); +} + +/** + * 有序key存取,性能测试 + */ +function ordKeyTest() { + let length = 1000000; + console.time('create') + let hashTable = new HashTable(); + for (let i = 0; i < length; i++) { + //24位长度有序key + hashTable.put({ + key: 'someTestSoSoSoSoLongKey' + i, + value: 'some value' + i + }); + } + console.timeEnd('create') + + let get = 100000; + console.time('get') + for (let j = 0; j < get; j++) { + let key = 'test' + Math.floor(Math.random() * 999999); + hashTable.get(key) + } + console.timeEnd('get') +} + +/** + * 无序key性能测试 + * 这个查找稍微有点不准,会有一定量随机字符串重复 + * 实际结果,创建没有区别,大数据量下由于无序key有一些会碰撞,get的总体用的时间会多不少。 + */ +function randKeyTest() { + let length = 1000000; + let keyList = []; + for (let i = 0; i < length; i++) { + keyList.push(randomString()); + } + console.time('create') + let hashTable = new HashTable(); + for (let i = 0; i < length; i++) { + hashTable.put({ + key: keyList[i], + value: 'some value' + i + }); + } + console.timeEnd('create') + let get = 100000; + console.time('get') + for (let j = 0; j < get; j++) { + let key = keyList[Math.floor(Math.random() * 999999)]; + hashTable.get(key) + } + console.timeEnd('get') +} + +/** + * 直接使用Object的性能测试 + * 有序就不测了,估计不会有区别,只看不使用hash的无序key + * 结果:想达到同样的结果创建会比hash后的慢接近四分之三,获取用时差不多 + */ +function randKeyTestFromObj() { + let length = 1000000; + let keyList = []; + for (let i = 0; i < length; i++) { + keyList.push(randomString()); + } + console.time('create') + let hashTable = {}; + for (let i = 0; i < length; i++) { + let key = keyList[i]; + hashTable[key] = { + key: key, + value: 'some value' + i + } + } + console.timeEnd('create') + let get = 100000; + console.time('get') + for (let j = 0; j < get; j++) { + let key = keyList[Math.floor(Math.random() * 999999)]; + hashTable[key] + } + console.timeEnd('get') +} +/** + * 直接使用Map的性能测试 + * 结果:创建用时差不多,但是获取快了一个数量级(十倍不止) + */ +function randKeyTestFromMap() { + let length = 1000000; + let keyList = []; + for (let i = 0; i < length; i++) { + keyList.push(randomString()); + } + console.time('create') + let hashTable = new Map(); + for (let i = 0; i < length; i++) { + let key = keyList[i]; + hashTable.set(key, { + key: key, + value: 'some value' + i + }) + } + console.timeEnd('create') + let get = 100000; + console.time('get') + for (let j = 0; j < get; j++) { + let key = keyList[Math.floor(Math.random() * 999999)]; + hashTable.get(key); + } + console.timeEnd('get') +} + +//生成指定长度的字符串 +function randomString(len) { + len = len || 24; + var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; + var maxPos = chars.length; + var pwd = ''; + for (i = 0; i < len; i++) { + pwd += chars.charAt(Math.floor(Math.random() * maxPos)); + } + return pwd; +} \ No newline at end of file diff --git a/javascript/23_tree/binary_tree.js b/javascript/23_tree/binary_tree.js new file mode 100644 index 00000000..f6e201f9 --- /dev/null +++ b/javascript/23_tree/binary_tree.js @@ -0,0 +1,228 @@ +class Node { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} +/** + * 搜索二叉树 + * 允许重复值添加 + * 实现有点弯弯绕 + */ +class SearchTree { + constructor() { + this.root = null; + } + insert(num) { + let node = new Node(num); + if (this.root === null) { + this.root = node; + return + } + let prent = this.getPrev(num); + if (num < prent.value) { + prent.left = node; + } else { + prent.right = node; + } + + } + remove(num) { + let point = this.root; + let prent = null; + let tree = this; + + let res = null; + while (true) { + if (point.left) { + if (num < point.left.value || num < point.value) { + prent = point; + point = point.left; + continue + } + } + if (point.right) { + if (num >= point.right.value || num >= point.value) { + if (num === point.value) { + delMethod(point, prent); + if (prent === null) { + point = this.root; + } else { + prent = prent; + point = prent.right; + } + res = true; + continue + } + prent = point; + point = point.right; + continue + } + } + if (point.value === num) { + res = true; + delMethod(point, prent); + } + break; + } + return res; + function delMethod(delNode, parent) { + let p = delNode; // p指向要删除的节点 + let pp = parent; // pp记录的是p的父节点 + + // 要删除的节点有两个子节点 + if (p.left != null && p.right != null) { // 查找右子树中最小节点 + let minP = p.right; + let minPP = p; // minPP表示minP的父节点 + while (minP.left != null) { + minPP = minP; + minP = minP.left; + } + p.value = minP.value; // 将minP的数据替换到p中 + p = minP; // 下面就变成了删除minP了 + pp = minPP; + } + + // 删除节点是叶子节点或者仅有一个子节点 + let child; // p的子节点 + if (p.left != null) child = p.left; + else if (p.right != null) child = p.right; + else child = null; + + if (pp == null) { + tree.root = child + } + else if (pp.left == p) { + pp.left = child; + } + else { + pp.right = child; + } + } + + } + //中序遍历 + print() { + let point = this.root; + if (point) { + printAll(point.left) + console.log(point.value); + printAll(point.right) + } + function printAll(point) { + if (point == null) { + return + } + printAll(point.left); + console.log(point.value); + printAll(point.right) + } + } + find(num) { + if (this.root === null) { + this.root = node; + return + } + return this.getPrev(num, true); + } + //添加和查找的公用部分 + getPrev(num, find = false) { + let point = this.root; + let res = []; + while (true) { + if (point.left) { + if (num < point.left.value || num < point.value) { + point = point.left + continue + } + } + + if (point.right) { + if (num >= point.right.value || num >= point.value) { + //搜索时如果有多个值则缓存 + if (find && num === point.value) { + res.push(point.value); + } + point = point.right; + continue + } + } + //如果是搜索 + if (find) { + if (point.value === num) { + res.push(point.value); + } + + if (res.length === 0) { + return null + } + + if (res.length === 1) { + return res[0]; + } + + return res; + } + //如果是添加 返回的是应该添加的那各节点的父节点 + return point; + } + } +} + + + +function baseTest() { + let searchTree = new SearchTree(); + console.log('step 1:') + searchTree.insert(4); + searchTree.insert(1); + searchTree.insert(2); + searchTree.insert(5); + + searchTree.print(); //1 2 4 5 + console.log('step 2:') + console.log('5', searchTree.find(5)) //5 + console.log('null:', searchTree.find(6)) //null + searchTree.insert(5); + searchTree.insert(5); + console.log('5,5,5:', searchTree.find(5)) + + +} +//删除测试 +function delTest() { + let searchTree = new SearchTree(); + console.log('add: 4 1 2 5 ') + searchTree.insert(4); + searchTree.insert(1); + searchTree.insert(2); + searchTree.insert(5); + searchTree.print(); //1 2 4 5 + //console.log('del 3 null:', searchTree.remove(3)); + console.log('del 1 true:', searchTree.remove(1)); + // console.log('print 2 4 5:') + // searchTree.print(); + // console.log('del 4 root true:', searchTree.remove(4)); + // console.log('print 2 5:') + // searchTree.print(); + // console.log('add: 3 7 1 5 5 5 ') + // searchTree.insert(3); + // searchTree.insert(7); + // searchTree.insert(1); + // searchTree.insert(5); + // searchTree.insert(5); + // searchTree.insert(5); + // console.log('print: 1 2 3 5 5 5 5 7 ') + // searchTree.print(); + // console.log('del 5 true:', searchTree.remove(5)); + // console.log('print: 1 2 3 7 ') + // searchTree.print(); +} + +delTest(); + + + + + diff --git a/javascript/28_heapsort/heap-sort.js b/javascript/28_heapsort/heap-sort.js new file mode 100644 index 00000000..572ddf06 --- /dev/null +++ b/javascript/28_heapsort/heap-sort.js @@ -0,0 +1,65 @@ +/** + * 堆排序 + * + * Author: nameczz + */ + +// 忽视数组的第一位 +class HeapSort { + constructor(originArray) { + this.originArray = originArray + console.log(this.originArray) + } + buildHeap() { + const arr = this.originArray + const startIndex = Math.floor(arr.length) + for (let i = startIndex; i >= 1; i--) { + this.heapify(arr, arr.length, i) + } + return arr + } + + heapify(arr, len, i) { + while (true) { + let maxPos = i + // 如果index i拥有叶左节点 并且左节点较大 + if (i * 2 <= len && arr[i] < arr[i * 2]) { + maxPos = i * 2 + } + // 如果index i拥有叶右节点 与Max节点比较大小,选出父/左/右中最大的一个 + if (i * 2 + 1 <= len && arr[maxPos] < arr[i * 2 + 1]) { + maxPos = i * 2 + 1 + } + if (maxPos === i) break // 循环直到i节点为最大值 + this.swap(arr, i, maxPos) // 交换位置, 父节点为父/左/右中最大的一个 + i = maxPos // i为左/右节点,并尝试向下查找更大的值 + } + } + + sort() { + const arr = this.buildHeap() // 先建堆 + let len = arr.length - 1 + while (len > 1) { + this.swap(arr, 1, len) // 交换顶元素和最后一位。顶元素永远是最大的。 + len-- + this.heapify(arr, len, 1) //剩下的元素重新建堆 直到len === 1 停止 + } + console.log(arr) + } + + swap(arr, i, max) { + let temp = arr[i] + arr[i] = arr[max] + arr[max] = temp + } +} + +const arr = [null] +let i = 0 +while (i <= 10) { + const num = Math.floor(Math.random() * 100) + arr.push(num) + i++ +} +const testHeap = new HeapSort(arr) +testHeap.sort() \ No newline at end of file diff --git a/javascript/28_heapsort/heap.js b/javascript/28_heapsort/heap.js new file mode 100644 index 00000000..ce420774 --- /dev/null +++ b/javascript/28_heapsort/heap.js @@ -0,0 +1,119 @@ +/** + * 优先队列的 堆实现 + */ +class HeapNode { + constructor(num, item) { + this.sortNum = num; + this.content = item; + } +} + +class Heap { + constructor(arr = []) { + this.PRIVATE = { + swap(arr, i, j) { + let temp = arr[i] + arr[i] = arr[j] + arr[j] = temp + }, + //从point往下 堆化 + heapify(point = 1) { + let { swap, store } = this; + while (true) { + let lPoint = point * 2; + let rPoint = point * 2 + 1; + if (store[lPoint] && store[point].sortNum < store[lPoint].sortNum) { + swap(store, point, lPoint); + point = lPoint; + continue + } + if (store[rPoint] && store[point].sortNum < store[rPoint].sortNum) { + swap(store, point, rPoint); + point = rPoint; + continue + } + break; + } + + }, + store: [null].concat(arr) + } + //建堆 + //从最后一个非子叶节点遍历 + for (let i = (this.PRIVATE.store.length / 2 | 0); i > 1; i--) { + this.PRIVATE.heapify(i); + } + + } + insert(node) { + let store = this.PRIVATE.store; + let HeapUtil = this.PRIVATE; + store.push(node); + + let point = store.length - 1; + let sub = point / 2 | 0; + while (sub > 0 && store[point].sortNum > store[sub].sortNum) { // 自下往上堆化 + HeapUtil.swap(store, point, sub); // swap()函数作用:交换下标为i和i/2的两个元素 + point = sub; + sub = sub / 2 | 0; + } + } + getMax() { + let store = this.PRIVATE.store; + let point = store.length - 1; + if (point === 0) { + return null; + } + let HeapUtil = this.PRIVATE; + //最大与末尾元素交换 + HeapUtil.swap(store, point, 1); + let max = store.pop(); + HeapUtil.heapify(); + return max; + } + +} + +function HeapTest() { + let maxHeap = new Heap(); + console.log('偶数个') + maxHeap.insert(new HeapNode(2, 'c')) + maxHeap.insert(new HeapNode(1, 'c')) + maxHeap.insert(new HeapNode(7, 'a')) + maxHeap.insert(new HeapNode(4, 'c')) + console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); + console.log('奇数个') + maxHeap.insert(new HeapNode(5, 'b')) + maxHeap.insert(new HeapNode(6, 'c')) + maxHeap.insert(new HeapNode(10, 'a')) + console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); + console.log('获取最大值:', maxHeap.getMax()); + console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); + console.log('获取最大值:', maxHeap.getMax()); + console.log('check:', isHeapArr(maxHeap.PRIVATE.store)); + +} +function createTest() { + console.log('随机创建测试:') + let arr = []; + let i = 0 + while (i <= 10) { + const num = Math.floor(Math.random() * 100) + arr.push(new HeapNode(num, i)) + i++ + } + let heap = new Heap(arr); + console.log('check:', isHeapArr(heap.PRIVATE.store)) +} + +function isHeapArr(arr) { + for (let i = 1; i < arr.length; i++) { + let p = arr[i]; + let l = arr[i * 2]; + let r = arr[i * 2 + 1]; + if (l > p || r > p) { + return false; + } + } + return true; +} diff --git a/javascript/35_trie/trie.js b/javascript/35_trie/trie.js new file mode 100644 index 00000000..8f3f4092 --- /dev/null +++ b/javascript/35_trie/trie.js @@ -0,0 +1,56 @@ + + +class TrieNode { + constructor(data){ + this.data = data; + this.children = new Array(26); + this.isEndingChar = false + } +} + +class TrieTree { + + constructor(data){ + this.root = new TrieNode('/') + } + + insert (text) { + let node = this.root; + for (let char of text) { + let index = char.charCodeAt() - 'a'.charCodeAt(); + if(!node.children[index]) { + node.children[index] = new TrieNode(char); + } + node = node.children[index]; + } + + node.isEndingChar = true; + } + + find (text) { + let node = this.root; + + for(let char of text) { + let index = char.charCodeAt() - 'a'.charCodeAt(); + if(node.children[index]) { + node = node.children[index]; + } else { + return false; + } + } + + return node.isEndingChar; + } +} + +var tree = new TrieTree(); +var strs = ["how", "hi", "her", "hello", "so", "see"]; +for(let str of strs) { + tree.insert(str); +} + +for(let str of strs) { + console.log(tree.find(str)); +} + +console.log(tree.find('world')); \ No newline at end of file diff --git a/javascript/36_ac_automata/ac_automata.js b/javascript/36_ac_automata/ac_automata.js new file mode 100644 index 00000000..06e40718 --- /dev/null +++ b/javascript/36_ac_automata/ac_automata.js @@ -0,0 +1,103 @@ + +MAX_LEN = 128; + +class ACNode { + constructor(data){ + this.data = data; + this.children = new Array(MAX_LEN); + this.isEndingChar = false; + this.length = 0; + this.fail = null; + } +} + +class ACTree { + + constructor(data){ + this.root = new ACNode('/') + } + + insert (text) { + let node = this.root; + for (let char of text) { + let index = char.charCodeAt() + 1; + if(!node.children[index]) { + node.children[index] = new ACNode(char); + } + node = node.children[index]; + } + + node.isEndingChar = true; + node.length = text.length; + } + + buildFailurePointer() { + let root = this.root; + let queue = []; + queue.push(root); + + while (queue.length > 0) { + let p = queue.shift(); + + for (let i = 0; i < MAX_LEN; i++) { + let pc = p.children[i]; + if (!pc) { + continue; + } + + if(p == root) { + pc.fail = root; + } else { + let q = p.fail; + while (q) { + let qc = q.children[pc.data.charCodeAt() + 1]; + if(qc) { + pc.fail = qc; + break; + } + q = q.fail; + } + if(!q) { + pc.fail = root; + } + } + queue.push(pc); + } + } + } + + match (text) { + let root = this.root; + let n = text.length; + let p = root; + + for(let i = 0; i < n; i++) { + let idx = text[i].charCodeAt() + 1; + while(!p.children[idx] && p != root){ + p = p.fail; + } + + p = p.children[idx]; + if(!p) { + p = root; + } + + let tmp = p; + while ( tmp != root) { + if (tmp.isEndingChar == true) { + console.log(`Start from ${i - p.length + 1}, length: ${p.length}`); + } + tmp = tmp.fail; + } + } + } +} + +let automata = new ACTree(); +let patterns = ["at", "art", "oars", "soar"]; +for (let pattern of patterns) { + automata.insert(pattern); +} + +automata.buildFailurePointer() +automata.match("soarsoars"); \ No newline at end of file diff --git a/javascript/36_ac_automata/ac_automata_unicode.js b/javascript/36_ac_automata/ac_automata_unicode.js new file mode 100644 index 00000000..0db9ba21 --- /dev/null +++ b/javascript/36_ac_automata/ac_automata_unicode.js @@ -0,0 +1,109 @@ + +class ACNode { + constructor(data){ + this.data = data; + this.children = new Map(); + this.isEndingChar = false; + this.length = 0; + this.fail = null; + } +} + +class ACTree { + + constructor(data){ + this.root = new ACNode('/') + } + + insert (text) { + let node = this.root; + for (let char of text) { + if(!node.children.get(char)) { + node.children.set(char, new ACNode(char)); + } + node = node.children.get(char); + } + + node.isEndingChar = true; + node.length = text.length; + } + + buildFailurePointer() { + let root = this.root; + let queue = []; + queue.push(root); + + while (queue.length > 0) { + let p = queue.shift(); + + for(var pc of p.children.values()){ + if (!pc) { + continue; + } + + if(p == root) { + pc.fail = root; + } else { + let q = p.fail; + while (q) { + let qc = q.children.get(pc.data); + if(qc) { + pc.fail = qc; + break; + } + q = q.fail; + } + if(!q) { + pc.fail = root; + } + } + queue.push(pc); + } + } + } + + match (text) { + let root = this.root; + let n = text.length; + let p = root; + + for(let i = 0; i < n; i++) { + let char = text[i]; + while(!p.children.get(char) && p != root){ + p = p.fail; + } + + p = p.children.get(char); + if(!p) { + p = root; + } + + let tmp = p; + while ( tmp != root) { + if (tmp.isEndingChar == true) { + console.log(`Start from ${i - p.length + 1}, length: ${p.length}`); + } + tmp = tmp.fail; + } + } + } +} + +function match( text, patterns) { + let automata = new ACTree(); + for (let pattern of patterns) { + automata.insert(pattern); + } + + automata.buildFailurePointer(); + automata.match(text); +} + +let patterns = ["at", "art", "oars", "soar"]; +let text = "soarsoars"; +match(text, patterns); + +let patterns2 = ["Fxtec Pro1", "谷歌Pixel"]; +let text2 = "一家总部位于伦敦的公司Fxtex在MWC上就推出了一款名为Fxtec Pro1的手机,该机最大的亮点就是采用了侧滑式全键盘设计。DxOMark年度总榜发布 华为P20 Pro/谷歌Pixel 3争冠"; +match(text2, patterns2); + diff --git a/javascript/42_dynamic_programming/levenshtein_distance.js b/javascript/42_dynamic_programming/levenshtein_distance.js new file mode 100644 index 00000000..e97f9713 --- /dev/null +++ b/javascript/42_dynamic_programming/levenshtein_distance.js @@ -0,0 +1,54 @@ +function lsDist(str1, str2) { + var n = str1.length; + var m = str2.length; + + var memo = new Array(n); + for(let i = 0; i < n; i++) { + memo[i] = new Array(m); + if (str1[i] === str2[0]) { + memo[i][0] = i - 0; + } else if(i === 0) { + memo[i][0] = 1; + } else { + memo[i][0] = memo[i - 1][0] + 1; + } + } + + for(let j = 0; j < m; j++) { + if(str1[0] === str2[j]) { + memo[0][j] = j - 0; + } else if(j === 0) { + memo[0][j] = 1; + } else { + memo[0][j] = memo[0][j - 1] + 1; + } + } + + for(let i = 1; i < n; i++) { + for(let j = 1; j < m; j++) { + if(str1[i] === str2[j]) { + memo[i][j] = Math.min(memo[i - 1][j] + 1, memo[i][j - 1] + 1, memo[i - 1][j - 1 ]); + } else { + memo[i][j] = Math.min(memo[i - 1][j] + 1, memo[i][j - 1] + 1, memo[i - 1][j - 1 ] + 1); + } + } + } + console.log(memo); + + return memo[n - 1][m - 1]; +} + + +var s = "mitcmu"; +var t = "mtacnu"; + +console.log( lsDist(s, t) ); + +var s = "kitten"; +var t = "sitting"; + +console.log( lsDist(s, t) ); + +var s = "flaw"; +var t = "lawn"; +console.log( lsDist(s, t) ); diff --git a/javascript/43_topological_sorting/dsf.js b/javascript/43_topological_sorting/dsf.js new file mode 100644 index 00000000..0f9016b1 --- /dev/null +++ b/javascript/43_topological_sorting/dsf.js @@ -0,0 +1,91 @@ + +function Graph() { + var graph = { + adj: new Map(), + addEdge: function (from, to){ + if(!this.adj.get(from)) { + this.adj.set(from, [ to ]); + } else { + this.adj.get(from).push(to); + } + }, + sortingByDsf: function(){ + var inverseAdj = new Map(); + var keys = this.adj.keys(); + for(let key of keys) { + let blk = this.adj.get(key); + if(blk) { + for(let v of blk) { + if(!inverseAdj.get(v)) { + inverseAdj.set(v, [key]); + } else { + inverseAdj.get(v).push(key); + } + } + } + } + + let inKeys = inverseAdj.keys(); + let vertexes = new Set([...keys, ...inKeys]); + let visited = []; + for(let vertex of vertexes) { + if(!visited.includes(vertex)) { + visited.push(vertex); + this.dsf(vertex, inverseAdj, visited); + } + } + }, + dsf: function(vertex, inverseAdj, visited) { + if(!inverseAdj.get(vertex)) { + inverseAdj.set(vertex, []); + } + + for(let v of inverseAdj.get(vertex)) { + if(visited.includes(v)) { + continue; + } + + visited.push(v); + + this.dsf(v, inverseAdj, visited); + } + + console.log("->" + vertex); + } + } + + return graph; +} + +var dag = new Graph(); +dag.addEdge(2, 1); +dag.addEdge(3, 2); +dag.addEdge(2, 4); +dag.addEdge(4, 1); +dag.sortingByDsf(); + + +var dag2 = new Graph(); +dag2.addEdge("main", "parse_options"); +dag2.addEdge("main", "tail_file"); +dag2.addEdge("main", "tail_forever"); +dag2.addEdge("tail_file", "pretty_name"); +dag2.addEdge("tail_file", "write_header"); +dag2.addEdge("tail_file", "tail"); +dag2.addEdge("tail_forever", "recheck"); +dag2.addEdge("tail_forever", "pretty_name"); +dag2.addEdge("tail_forever", "write_header"); +dag2.addEdge("tail_forever", "dump_remainder"); +dag2.addEdge("tail", "tail_lines"); +dag2.addEdge("tail", "tail_bytes"); +dag2.addEdge("tail_lines", "start_lines"); +dag2.addEdge("tail_lines", "dump_remainder"); +dag2.addEdge("tail_lines", "file_lines"); +dag2.addEdge("tail_lines", "pipe_lines"); +dag2.addEdge("tail_bytes", "xlseek"); +dag2.addEdge("tail_bytes", "start_bytes"); +dag2.addEdge("tail_bytes", "dump_remainder"); +dag2.addEdge("tail_bytes", "pipe_bytes"); +dag2.addEdge("file_lines", "dump_remainder"); +dag2.addEdge("recheck", "pretty_name"); +dag2.sortingByDsf(); diff --git a/javascript/45_bitmap/bitmap.js b/javascript/45_bitmap/bitmap.js new file mode 100644 index 00000000..2cd14b33 --- /dev/null +++ b/javascript/45_bitmap/bitmap.js @@ -0,0 +1,44 @@ + +class BitMap { + constructor(n) { + this.nbits = n; + this.blk = new Array(Math.floor(n / 16) + 1); + this.blk.fill(0); + } + + get(k) { + if( k > this.nbits) return false; + + let byteIndex = Math.floor(k / 16); + let bitIndex = k % 16; + + return !((this.blk[byteIndex] & (1 << bitIndex)) === 0); + } + + set(k) { + if( k > this.nbits) return; + + let byteIndex = Math.floor(k / 16); + let bitIndex = k % 16; + + this.blk[byteIndex] = this.blk[byteIndex] | (1 << bitIndex); + + } +} + +let aBitMap = new BitMap(20); + +aBitMap.set(1); +aBitMap.set(3); +aBitMap.set(5); +aBitMap.set(7); +aBitMap.set(9); +aBitMap.set(11); +aBitMap.set(13); +aBitMap.set(15); +aBitMap.set(17); +aBitMap.set(19); + +for(let i = 0; i < 21; i++) { + console.log(aBitMap.get(i)); +} \ No newline at end of file diff --git a/kotlin/05_array/ArrayKt.kt b/kotlin/05_array/ArrayKt.kt new file mode 100644 index 00000000..9606866f --- /dev/null +++ b/kotlin/05_array/ArrayKt.kt @@ -0,0 +1,74 @@ +import kotlin.Array + +/** + * 1) 数组的插入、删除、按照下标随机访问操作; + * 2)数组中的数据是int类型的; + * + * Author: Zackratos + */ + +class ArrayKt constructor(private val capacity: Int) { + // 定义整型数据data保存数据 + private val data: IntArray = IntArray(capacity) + // 定义数组中实际个数 + private var count: Int = 0 + + companion object { + @JvmStatic + fun main(args: Array) { + val array = ArrayKt(5) + array.printAll() + array.insert(0, 3) + array.insert(0, 4) + array.insert(1, 5) + array.insert(3, 9) + array.insert(3, 10) + array.printAll() + } + } + + // 根据索引,找到数据中的元素并返回 + fun find(index: Int): Int { + if (index !in 0..(count - 1)) return -1 + return data[index] + } + + // 插入元素:头部插入,尾部插入 + fun insert(index: Int, value: Int): Boolean { + // 数组空间已满 + if (count == capacity) { + System.out.println("没有可插入的位置") + return false + } + // 如果count还没满,那么就可以插入数据到数组中 + // 位置不合法 + if (index !in 0..count) { + System.out.println("位置不合法") + return false + } + // 位置合法 + (count downTo index + 1).forEach { + data[it] = data[it - 1] + } + data[index] = value + ++count + return true + } + + // 根据索引,删除数组中元素 + fun delete(index: Int): Boolean { + if (index !in 0..(count - 1)) return false + (index + 1 until count).forEach { + data[it - 1] = data[it] + } + --count + return true + } + + fun printAll() { + (0 until count).forEach { + System.out.println("${data[it]} ") + } + } + +} \ No newline at end of file diff --git a/kotlin/05_array/DynamicArray.kt b/kotlin/05_array/DynamicArray.kt new file mode 100644 index 00000000..38625d21 --- /dev/null +++ b/kotlin/05_array/DynamicArray.kt @@ -0,0 +1,118 @@ +/** + * 动态扩容的数组 + */ +class DynamicArray { + companion object { + // 默认容量 + const val DEFAULT_CAPACITY = 10 + + // 最大容量 + const val MAX_CAPACITY = Int.MAX_VALUE + } + + // 当前已使用大小 + private var usedSize = 0 + + // 当前容量大小 + private var capacity = 0 + + // 数组容器 + private var data: Array + + init { + this.capacity = DEFAULT_CAPACITY + this.data = Array(this.capacity) { 0 } + } + + /** + * 增加元素 + */ + fun add(value: Int) { + if (this.usedSize == this.capacity - 1) { + this.doubleCapacity() + } + this.data[this.usedSize] = value + ++this.usedSize + } + + /** + * 移除元素 + */ + fun remove(value: Int) { + if (this.usedSize >= 0) { + var target = -1 + + // 查找目标所在位置 + for (i in 0 until this.usedSize) { + if (this.data[i] == value) { + target = i + break + } + } + + // 找到了 + if (target >= 0) { + val size = this.usedSize - 1 + + // 把后续元素往前搬 + for (i in target until size) { + this.data[i] = this.data[i + 1] + } + + // 最后一个元素位置置为空 + this.data[size] = 0 + + // 更新已使用大小 + this.usedSize = size + } + } + } + + /** + * 通过索引设置元素的值 + */ + fun set(index: Int, value: Int) { + if (this.checkIndex(index)) { + this.data[index] = value + return + } + + throw IllegalArgumentException("index must be in rang of 0..${this.usedSize}") + } + + /** + * 获取元素 + */ + fun get(index: Int): Int? { + if (this.checkIndex(index)) { + return this.data[index] + } + + throw IllegalArgumentException("index must be in rang of 0..${this.usedSize}") + } + + /** + * 获取当前数组的大小 + */ + fun getSize(): Int = this.usedSize + + private fun checkIndex(index: Int): Boolean { + return index >= 0 && index < this.usedSize + } + + /** + * 按原容量的两倍进行扩容 + */ + private fun doubleCapacity() { + if (this.capacity < MAX_CAPACITY) { + this.capacity = Math.min(this.capacity * 2, MAX_CAPACITY) + val newArray = Array(this.capacity) { 0 } + + for (i in 0 until this.usedSize) { + newArray[i] = this.data[i] + } + + this.data = newArray + } + } +} \ No newline at end of file diff --git a/kotlin/06_linkedlist/SinglyLinkedList.kt b/kotlin/06_linkedlist/SinglyLinkedList.kt new file mode 100644 index 00000000..35fcec21 --- /dev/null +++ b/kotlin/06_linkedlist/SinglyLinkedList.kt @@ -0,0 +1,293 @@ +/** + * 1)单链表的插入、删除、查找操作; + * 2)链表中存储的是int类型的数据; + * + * Author:Zackratos + */ + +class SinglyLinkedList { + + private var head: Node? = null + + companion object { + @JvmStatic + fun main(args: Array) { + + val link = SinglyLinkedList() + println("hello") + val data = intArrayOf(1, 2, 5, 3, 1) + + for (i in data.indices) { + //link.insertToHead(data[i]); + link.insertTail(data[i]) + } + + println("打印原始:") + link.printAll() + if (link.palindrome()) { + println("回文") + } else { + println("不是回文") + } + } + } + + fun findByValue(value: Int): Node? { + var p = head + while (p != null && p.data != value) { + p = p.next + } + + return p + } + + fun findByIndex(index: Int): Node? { + var p = head + var pos = 0 + while (p != null && pos != index) { + p = p.next + ++pos + } + + return p + } + + //无头结点 + //表头部插入 + //这种操作将于输入的顺序相反,逆序 + fun insertToHead(value: Int) { + val newNode = Node(value, null) + insertToHead(newNode) + } + + fun insertToHead(newNode: Node) { + if (head == null) { + head = newNode + } else { + newNode.next = head + head = newNode + } + } + + //顺序插入 + //链表尾部插入 + fun insertTail(value: Int) { + + val newNode = Node(value, null) + //空链表,可以插入新节点作为head,也可以不操作 + if (head == null) { + head = newNode + + } else { + var q = head + while (q?.next != null) { + q = q.next + } + newNode.next = q?.next + q?.next = newNode + } + } + + fun insertAfter(p: Node?, value: Int) { + val newNode = Node(value, null) + insertAfter(p, newNode) + } + + fun insertAfter(p: Node?, newNode: Node) { + if (p == null) return + + newNode.next = p.next + p.next = newNode + } + + fun insertBefore(p: Node?, value: Int) { + val newNode = Node(value, null) + insertBefore(p, newNode) + } + + fun insertBefore(p: Node?, newNode: Node) { + if (p == null) return + if (head === p) { + insertToHead(newNode) + return + } + + var q = head + while (q != null && q.next !== p) { + q = q.next + } + + if (q == null) { + return + } + + newNode.next = p + q.next = newNode + + } + + fun deleteByNode(p: Node?) { + if (p == null || head == null) return + + if (p === head) { + head = head?.next + return + } + + var q = head + while (q != null && q.next !== p) { + q = q.next + } + + if (q == null) { + return + } + + q.next = q.next?.next + } + + fun deleteByValue(value: Int) { + if (head == null) return + + var p = head + var q: Node? = null + while (p != null && p.data != value) { + q = p + p = p.next + } + + if (p == null) return + + if (q == null) { + head = head?.next + } else { + q.next = q.next?.next + } + } + + fun printAll() { + var p = head + while (p != null) { + print("${p.data} ") + p = p.next + } + println() + } + + //判断true or false + fun TFResult(left: Node?, right: Node?): Boolean { + var l: Node? = left + var r: Node? = right + + println("left_:${l?.data}") + println("right_:${r?.data}") + while (l != null && r != null) { + if (l.data == r.data) { + l = l.next + r = r.next + continue + } else { + break + } + + } + + println("什么结果") + return l == null && r == null + } + + // 判断是否为回文 + fun palindrome(): Boolean { + if (head == null) { + return false + } else { + println("开始执行找到中间节点") + var p = head + var q = head + if (p?.next == null) { + println("只有一个元素") + return true + } + while (q?.next != null && q.next?.next != null) { + p = p?.next + q = q.next?.next + } + + println("中间节点${p?.data}") + println("开始执行奇数节点的回文判断") + val leftLink: Node? + val rightLink: Node? + if (q?.next == null) { + // p 一定为整个链表的中点,且节点数目为奇数 + rightLink = p?.next + leftLink = inverseLinkList(p)?.next + println("左边第一个节点${leftLink?.data}") + println("右边第一个节点${rightLink?.data}") + + } else { + //p q 均为中点 + rightLink = p?.next + leftLink = inverseLinkList(p) + } + return TFResult(leftLink, rightLink) + + } + } + + //带结点的链表翻转 + fun inverseLinkList_head(p: Node): Node { + // Head 为新建的一个头结点 + val Head = Node(9999, null) + // p 为原来整个链表的头结点,现在Head指向 整个链表 + Head.next = p + /* + 带头结点的链表翻转等价于 + 从第二个元素开始重新头插法建立链表 + */ + var Cur = p.next + p.next = null + var next: Node? + + while (Cur != null) { + next = Cur.next + Cur.next = Head.next + Head.next = Cur + println("first " + Head.data) + + Cur = next + } + + // 返回左半部分的中点之前的那个节点 + // 从此处开始同步像两边比较 + return Head + + } + + //无头结点的链表翻转 + fun inverseLinkList(p: Node?): Node? { + + var pre: Node? = null + var r = head + println("z---${r?.data}") + var next: Node? + while (r !== p) { + next = r?.next + + r?.next = pre + pre = r + r = next + } + + r?.next = pre + // 返回左半部分的中点之前的那个节点 + // 从此处开始同步像两边比较 + return r + + } + + fun createNode(value: Int): Node = Node(value, null) + + + + class Node(var data: Int, var next: Node?) +} \ No newline at end of file diff --git a/kotlin/07_linkedlist/LinkedListAlgo.kt b/kotlin/07_linkedlist/LinkedListAlgo.kt new file mode 100644 index 00000000..7648143e --- /dev/null +++ b/kotlin/07_linkedlist/LinkedListAlgo.kt @@ -0,0 +1,108 @@ +/** + * 1) 单链表反转 + * 2) 链表中环的检测 + * 3) 两个有序的链表合并 + * 4) 删除链表倒数第n个结点 + * 5) 求链表的中间结点 + * + * Author: Zackratos + */ + +object LinkedListAlgo { + + // 单链表反转 + fun reverse(list: Node): Node? { + var curr: Node? = list + var pre: Node? = null + while (curr != null) { + val next = curr.next + curr.next = pre + pre = curr + curr = next + } + return pre + } + + // 检测环 + fun checkCircle(list: Node): Boolean { + var fast = list.next + var slow: Node? = list + while (fast != null && slow != null) { + fast = fast.next?.next + slow = slow.next + if (fast === slow) + return true + } + + return false + } + + // 有序链表合并 + fun mergeSortedLists(la: Node, lb: Node): Node? { + var p: Node? = la + var q: Node? = lb + val head: Node? + if (p?.data ?: 0 < q?.data ?: 0) { + head = p + p = p?.next + } else { + head = q + q = q?.next + } + var r = head + while (p != null && q != null) { + if (p.data < q.data) { + r?.next = p + p = p.next + } else { + r?.next = q + q = q.next + } + r = r?.next + } + if (p != null) { + r?.next = p + } else { + r?.next = q + } + return head + } + + // 删除倒数第 k 个结点 + fun deleteLastKth(list: Node, k: Int): Node? { + var fast: Node? = list + var i = 1 + while (fast != null && i < k) { + fast = fast.next + i++ + } + if (fast == null) { + return when (i) { + k -> list.next + else -> list + } + } + var slow: Node? = list + var pre: Node? = null + while (fast != null) { + fast = fast.next + pre = slow + slow = slow?.next + } + pre?.next = pre?.next?.next + return list + } + + // 求中间结点 + fun findMiddleNode(list: Node): Node? { + var fast: Node? = list + var slow: Node? = list + while (fast?.next != null && fast.next?.next != null) { + fast = fast.next?.next + slow = slow?.next + } + return slow + } + + class Node(var data: Int, var next: Node?) +} \ No newline at end of file diff --git a/kotlin/08_stack/StackBasedOnLinkedList.kt b/kotlin/08_stack/StackBasedOnLinkedList.kt new file mode 100644 index 00000000..76ea5452 --- /dev/null +++ b/kotlin/08_stack/StackBasedOnLinkedList.kt @@ -0,0 +1,36 @@ +/** + * 基于链表实现的栈。 + * + * Author: Zackratos + */ + +class StackBasedOnLinkedList { + + private var top: Node? = null + + fun push(value: Int) { + val newNode = Node(value, null) + // 不管 top 是不是 null,都可以这么写 + newNode.next = top + top = newNode + } + + fun pop(): Int { + if (top == null) return -1 + val node = top + top = top!!.next + return node!!.data + } + + fun printAll() { + var p = top + while (p != null) { + print("${p.data} ") + p = p.next + } + println() + } + + class Node(var data: Int, var next: Node?) + +} \ No newline at end of file diff --git a/notes/.gitkeep b/notes/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/10_recursion/readme.md b/notes/10_recursion/readme.md new file mode 100644 index 00000000..29478b1c --- /dev/null +++ b/notes/10_recursion/readme.md @@ -0,0 +1,28 @@ +# 递归 + +## 三个条件 + +* 可分解为子问题 +* 子问题与原问题解法一致,只有规模上的不同 +* 有终止条件 + +## 写递归代码 + +* 整理出递推公式 +* 确定好终止条件 +* 「翻译」成代码 + +关键: + +> 只要遇到递归,我们就把它抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑去分解每一个步骤。 + +## 警惕 + +* 堆栈溢出 <- 递归深度过大 +* 重复计算 <- 递归过程中的不同分支,重复计算相同子问题 + * 保存子问题结果(map/dict) +* 空间复杂度高 <- 递归函数调用带来的消耗 + +## 递归改写非递归 + +本质:人肉模拟函数调用堆栈。 diff --git a/notes/11_sorts/readme.md b/notes/11_sorts/readme.md new file mode 100644 index 00000000..ac0b1342 --- /dev/null +++ b/notes/11_sorts/readme.md @@ -0,0 +1,100 @@ +# 排序(平方时间复杂度排序算法) + +| 排序算法 | 时间复杂度 | 是否基于比较 | +|---------|----|----| +| 冒泡、插入、选择 | $O(n^2)$ | [y] | +| 快排、归并 | $O(n\log n)$ | [y] | +| 桶、基数、计数 | $O(n) | [x] | + +开篇问题:插入排序和冒泡排序的时间复杂度相同,都是 $O(n^2)$,在实际软件开发中,为什么我们更倾向于使用插入排序而不是冒泡排序? + +## 如何分析「排序算法」? + +### 算法执行效率 + +1. 最好、最坏、平均情况的时间复杂度 +2. 时间复杂度的系数、低阶、常数——在渐进复杂度相同的情况下,需要比较系数、低阶和常数 +3. 比较和交换(移动)的次数——基于比较的排序算法的两种基本操作 + +### 算法的内存消耗 + +是否为原地排序算法(In-place sort algorithm),即算法的空间复杂度是否为 $O(1)$。 + +### 排序的稳定性 + +经过排序算法处理后,值相同的元素,在原序列和排序后序列中的相对位置保持不变,则称该排序算法是稳定的。 + +> 待排序的 `item` 并不是简单的值,而是一个基于对象中的某个 `key` 进行排序时,排序的稳定性就有意义了。 + +## 冒泡排序 + +* 每次循环都从序列起始位置开始 +* 循环中的每个动作,都对比相邻两个元素的大小是否满足偏序要求,若不满足,则交换顺序 + +![冒泡排序例图](https://static001.geekbang.org/resource/image/88/34/8890cbf63ea80455ce82490a23361134.jpg) + +分析: + +* 原地排序 +* 稳定排序(偏序关系是严格的偏序关系,如 `<` 或 `>`) +* 时间复杂度 + * 最好 $O(n)$ + * 最坏 $O(n^2)$ + * 平均 $O(n^2)$ + +### 冒泡排序的平均时间复杂度非严格分析 + +* 有序度:序列中满足偏序关系的两两组合的元素对的个数 +* 满有序度:排序完成的序列的有序度,它等于 $n(n - 1) / 2$ +* 逆序度:序列中不满足偏序关系的亮亮组合的元素对的个数 + +显然,$\text{逆序度} = \text{满有序度} - \text{有序度}$。 + +在冒泡排序中,每产生一次「交换」操作,$\text{逆序度}--$。于是,平均情况下,需要 $n(n - 1)/4$ 次交换操作,它已经是 $O(n^2)$ 了。因此,尽管比较操作的数量会大于交换操作的数量,但我们依然能说,冒泡排序的平均时间复杂度是 $O(n^2)$。 + +> 分析过程不严格,但足够说明问题。 + +## 插入排序 + +1. 将待排序数列分为已排序区间和未排序区间 +2. 取未排序区间的第一个元素 +3. 遍历已排序区间,按照偏序关系,寻找合适的位置,插入未排序区间的第一个元素 +4. 重复 2 -- 3 直至未排序区间长度为零 + +![插入排序例图](https://static001.geekbang.org/resource/image/fd/01/fd6582d5e5927173ee35d7cc74d9c401.jpg) + +分析: + +* 原地排序 +* 稳定排序(值相同的元素,往后插) +* 时间复杂度 + * 最好 $O(n)$ + * 最坏 $O(n^2)$ + * 平均 $O(n^2)$(乘法法则) + +## 选择排序 + +1. 将待排序数列分为已排序区间和未排序区间 +2. 遍历未排序区间,取未排序区间的最小元素 +3. 交换上述最小元素与未排序区间中的第一个元素的位置 +4. 重复 2 -- 3 直至未排序区间长度为零 + +![选择排序例图](https://static001.geekbang.org/resource/image/32/1d/32371475a0b08f0db9861d102474181d.jpg) + +分析: + +* 非原地排序 +* 非稳定排序 +* 时间复杂度 + * 最好 $O(n^2)$ + * 最坏 $O(n^2)$ + * 平均 $O(n^2)$(乘法法则) + +## 开篇问题 + +* 对同一份未排序序列数据,冒泡排序和插入排序所需的交换(移动)次数是一定的,且是相等的 +* 单次数据交换,冒泡排序所需的时间更长(三次赋值操作,插排只需要一次) + +另有插入排序的优化版本[希尔排序](https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F)。 + +![小结](https://static001.geekbang.org/resource/image/34/50/348604caaf0a1b1d7fee0512822f0e50.jpg) diff --git a/notes/12_sorts/.gitkeep b/notes/12_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/12_sorts/readme.md b/notes/12_sorts/readme.md new file mode 100644 index 00000000..57066e44 --- /dev/null +++ b/notes/12_sorts/readme.md @@ -0,0 +1,176 @@ +# 排序(线性对数时间复杂度排序算法) + +开篇问题:如何在 $O(n)$ 时间复杂度内寻找一个无序数组中第 K 大的元素? + +## 归并排序 + +* 归并排序使用了「分治」思想(Divide and Conquer) + * 分:把数组分成前后两部分,分别排序 + * 合:将有序的两部分合并 + +![归并排序分解图](https://static001.geekbang.org/resource/image/db/2b/db7f892d3355ef74da9cd64aa926dc2b.jpg) + +* 分治与递归 + * 分治:解决问题的处理办法 + * 递归:实现算法的手段 + * ——分治算法经常用递归来实现 +* 递归实现: + * 终止条件:区间 `[first, last)` 内不足 2 个元素 + * 递归公式:`merge_sort(first, last) = merge(merge_sort(first, mid), merge_sort(mid, last))`,其中 `mid = first + (last - first) / 2` + +C++ 实现: + +```cpp +template ::value_type, + typename BinaryPred = std::less> +void merge_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) { + const auto len = std::distance(first, last); + if (len <= 1) { return; } + auto cut = first + len / 2; + merge_sort(first, cut, comp); + merge_sort(cut, last, comp); + std::vector tmp; + tmp.reserve(len); + detail::merge(first, cut, cut, last, std::back_inserter(tmp), comp); + std::copy(tmp.begin(), tmp.end(), first); +} +``` + +这里涉及到一个 `merge` 的过程,它的实现大致是: + +```cpp +namespace detail { +template ::value_type>> +OutputIt merge(InputIt1 first1, InputIt1 last1, + InputIt2 first2, InputIt2 last2, + OutputIt d_first, + BinaryPred comp = BinaryPred()) { + for (; first1 != last1; ++d_first) { + if (first2 == last2) { + return std::copy(first1, last1, d_first); + } + if (comp(*first2, *first1)) { + *d_first = *first2; + ++first2; + } else { + *d_first = *first1; + ++first1; + } + } + return std::copy(first2, last2, d_first); +} +} // namespace detail +``` + +![`merge` 的过程](https://static001.geekbang.org/resource/image/95/2f/95897ade4f7ad5d10af057b1d144a22f.jpg) + +### 算法分析 + +* 稳定性 + * 由于 `comp` 是严格偏序,所以 `!comp(*first2, *first1)` 时,取用 `first1` 的元素放入 `d_first` 保证了算法稳定性 +* 时间复杂度 + * 定义 $T(n)$ 表示问题规模为 $n$ 时算法的耗时, + * 有递推公式:$T(n) = 2T(n/2) + n$ + * 展开得 $T(n) = 2^{k}T(1) + k * n$ + * 考虑 $k$ 是递归深度,它的值是 $\log_2 n$,因此 $T(n) = n + n\log_2 n$ + * 因此,归并排序的时间复杂度为 $\Theta(n\log n)$ +* 空间复杂度 + * 一般来说,空间复杂度是 $\Theta(n)$ + +## 快速排序(quick sort,快排) + +原理: + +* 在待排序区间 `[first, last)` 中选取一个元素,称为主元(pivot,枢轴) +* 对待排序区间进行划分,使得 `[first, cut)` 中的元素满足 `comp(element, pivot)` 而 `[cut, last)` 中的元素不满足 `comp(element, pivot)` +* 对划分的两个区间,继续划分,直到区间 `[first, last)` 内不足 2 个元素 + +![快排分区示例](https://static001.geekbang.org/resource/image/4d/81/4d892c3a2e08a17f16097d07ea088a81.jpg) + +显然,这又是一个递归: + +* 终止条件:区间 `[first, last)` 内不足 2 个元素 +* 递归公式:`quick_sort(first, last) = quick_sort(first, cut) + quick_sort(cut, last)` + +```cpp +template ::value_type> +void quick_sort(IterT first, IterT last) { + if (std::distance(first, last) > 1) { + IterT prev_last = std::prev(last); + IterT cut = std::partition(first, prev_last, [prev_last](T v) { return v < *prev_last; }); + std::iter_swap(cut, prev_last); + quick_sort(first, cut); + quick_sort(cut, last); + } +} +``` + +> 一点优化(Liam Huang):通过将 `if` 改为 `while` 同时修改 `last` 迭代器的值,可以节省一半递归调用的开销。 + +```cpp +template ::value_type> +void quick_sort(IterT first, IterT last) { + while (std::distance(first, last) > 1) { + IterT prev_last = std::prev(last); + IterT cut = std::partition(first, prev_last, [prev_last](T v) { return v < *prev_last; }); + std::iter_swap(cut, prev_last); + quick_sort(cut, last); + last = cut; + } +} +``` + +如果不要求空间复杂度,分区函数实现起来很容易。 + +![非原地分区](https://static001.geekbang.org/resource/image/66/dc/6643bc3cef766f5b3e4526c332c60adc.jpg) + +若要求原地分区,则不那么容易了。下面的实现实现了原地分区函数,并且能将所有相等的主元排在一起。 + +```cpp +template ::value_type, + typename Compare = std::less> +std::pair inplace_partition(BidirIt first, + BidirIt last, + const T& pivot, + Compare comp = Compare()) { + BidirIt last_less, last_greater, first_equal, last_equal; + for (last_less = first, last_greater = first, first_equal = last; + last_greater != first_equal; ) { + if (comp(*last_greater, pivot)) { + std::iter_swap(last_greater++, last_less++); + } else if (comp(pivot, *last_greater)) { + ++last_greater; + } else { // pivot == *last_greater + std::iter_swap(last_greater, --first_equal); + } + } + const auto cnt = std::distance(first_equal, last); + std::swap_ranges(first_equal, last, last_less); + first_equal = last_less; + last_equal = first_equal + cnt; + return {first_equal, last_equal}; +} +``` + +### 算法分析 + +* 稳定性 + * 由于 `inplace_partition` 使用了大量 `std::iter_swap` 操作,所以不是稳定排序 +* 时间复杂度 + * 定义 $T(n)$ 表示问题规模为 $n$ 时算法的耗时, + * 有递推公式:$T(n) = 2T(n/2) + n$(假定每次分割都是均衡分割) + * 展开得 $T(n) = 2^{k}T(1) + k * n$ + * 考虑 $k$ 是递归深度,它的值是 $\log_2 n$,因此 $T(n) = n + n\log_2 n$ + * 因此,快速排序的时间复杂度为 $\Theta(n\log n)$ +* 空间复杂度 + * 一般来说,空间复杂度是 $\Theta(1)$,因此是原地排序算法 + +## 开篇问题 + +* 分区,看前半段元素数量 + * 前半段元素数量 < K,对后半段进行分区 + * 前半段元素数量 > K,对前半段进行分区 + * 前半段元素数量 = K,前半段末位元素即是所求 diff --git a/notes/13_sorts/.gitkeep b/notes/13_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/13_sorts/readme.md b/notes/13_sorts/readme.md new file mode 100644 index 00000000..aa38c893 --- /dev/null +++ b/notes/13_sorts/readme.md @@ -0,0 +1,77 @@ +# 线性排序 + +## 开篇问题 + +如何按年龄给 100 万用户排序? + +## 桶排序(Bucket Sort) + +算法思想: + +* 按待排序数据的 key 分有序桶 +* 桶内排序 +* 有序桶依次输出 + +![桶排序示例](https://static001.geekbang.org/resource/image/98/ae/987564607b864255f81686829503abae.jpg) + +### 算法分析 + +* 时间复杂度 $O(n)$ + * $n$ 个元素,分 $m$ 个有序桶,每个桶里平均 $k = n / m$ 个元素 + * 桶内快排,复杂度 $O(k \log k)$,$m$ 个桶一共 $O(n \log k)$ + * 当 $m$ 接近 $n$,例如当 $k = 4$ 时,这个复杂度近似 $O(n)$ +* 使用条件 + * 数据易于分如有序桶 + * 数据在各个有序桶之间分布均匀 + * 适合外部排序——数据不全部载入磁盘 + +## 计数排序(Counting Sort) + +计数排序可以视作是桶排序的一个特殊情况: + +* 数据的取值范围很小 +* 每个分桶内的元素 key 值都一样 + +此时,由于分桶内的元素 key 值都一样,所以桶内的排序操作可以省略,以及桶的编号本身就能记录桶内元素的值。因此,算法只需遍历一遍所有的数据,统计每个取值上有多少元素即可。这个过程时间复杂度是 $O(n)$。 + +* 假设待排序的数组 `A = {2, 5, 3, 0, 2, 3, 0, 3}`,我们有计数数组 `C = {2, 0, 2, 3, 0, 1}` + +接下来,我们要对 `C` 进行计数操作,具体来说,对从下标为 1 的元素开始累加 `C[i] += C[i - 1]`。 + +* 计数累加 `C = {2, 2, 4, 7, 7, 8}` + +此时,`C` 中的元素表示「小于等于下标的元素的个数」。接下来,我们从尾至头扫描待排序数组 `A`,将其中元素依次拷贝到输出数组 `R` 的相应位置。我们注意到,`A[7] = 3` 而 `C[3] == 4` 。这意味着,待排序的数组中,包括 3 本身在内,不超过 3 的元素共有 4 个。因此,我们可以将这个 3 放置在 `R[C[3] - 1]` 的位置,而后将 `C[3]` 的计数减一——这是由于待排序数组中未处理的部分,不超过 3 的元素现在只剩下 3 个了。如此遍历整个待排序数组 `A`,即可得到排序后的结果 `R`。 + +![计数排序示例](https://static001.geekbang.org/resource/image/1d/84/1d730cb17249f8e92ef5cab53ae65784.jpg) + +### 算法分析 + +* 时间复杂度 + * $n$ 个元素,最大值是 $k$,分 $k$ 个「桶」;时间复杂度 $O(n)$ + * 桶内计数累加;时间复杂度 $O(k)$ + * 摆放元素;时间复杂度 $O(n)$ + * 当 $k < n$ 时,总体时间复杂度是 $O(n)$ +* 使用条件 + * $k < n$ + * 待排序数据的 key 是非负整数 + +## 基数排序(Radix Sort) + +基数排序适用于等长数据的排序。对于不等长数据,可以在较短的数据后面做 padding,使得数据等长。 + +* 先就 least significant digit 进行稳定排序——通常可以用桶排序或者计数排序;时间复杂度 $O(n)$ +* 而后依次向 greatest significant digit 移动,进行稳定排序 + +![基数排序示例](https://static001.geekbang.org/resource/image/df/0c/df0cdbb73bd19a2d69a52c54d8b9fc0c.jpg) + +### 算法分析 + +* 时间复杂度 + * 对每一位的排序时间复杂度是 $O(n)$ + * 总共 $k$ 位,因此总的时间复杂度是 $O(kn)$;考虑到 $k$ 是常数,因此总的时间复杂度是 $O(n)$ +* 使用条件 + * 等长数据 + +## 解答开篇 + +桶排序。 diff --git a/notes/14_sorts/.gitkeep b/notes/14_sorts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/14_sorts/readme.md b/notes/14_sorts/readme.md new file mode 100644 index 00000000..49e8d941 --- /dev/null +++ b/notes/14_sorts/readme.md @@ -0,0 +1,10 @@ +# 排序优化 + +## 如何取舍排序算法? + +* 排序规模小 —— $O(n^2)$ 的算法(通常是插排) +* 排序规模大 —— $O(n\log n)$ 的算法(通常不用归并排序) + +## 如何优化快速排序? + +参考:[谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/) diff --git a/notes/15_bsearch/.gitkeep b/notes/15_bsearch/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/15_bsearch/readme.md b/notes/15_bsearch/readme.md new file mode 100644 index 00000000..4947b03a --- /dev/null +++ b/notes/15_bsearch/readme.md @@ -0,0 +1,23 @@ +# 二分查找(上) + +## 算法描述 + +二分查找(Binary Search)也叫折半查找,是针对有序数据集合的查找算法。其描述十分简单: + +* 折半取中,判断元素与目标元素的大小关系 + * 小于——往前继续折半 + * 大于——往后继续折半 + * 等于——返回 + +关于它的复杂度分析,参见[谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/)中的相关信息。它的复杂度是 $O(\log n)$。 + +## $O(\log n)$ 的惊人之处 + +在 42 亿个数据中用二分查找一个数据,最多需要比较 32 次。 + +## 适用场景 + +* 依赖顺序表结构 +* 数据本身必须有序 +* 数据量相对比较元素的开销要足够大——不然遍历即可 +* 数据量相对内存空间不能太大——不然顺序表装不下 diff --git a/notes/16_bsearch/.gitkeep b/notes/16_bsearch/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/16_bsearch/readme.md b/notes/16_bsearch/readme.md new file mode 100644 index 00000000..541b35f8 --- /dev/null +++ b/notes/16_bsearch/readme.md @@ -0,0 +1,100 @@ +# 二分查找(下) + +本节课讨论二分的各种变体。实际上在针对上一节的代码中,已经实现了两个变体。本次实现四个变体: + +* 第一个等于给定值的元素 +* 最后一个等于给定值的元素 +* 第一个不小于给定值的元素 +* 最后一个不大于给定值的元素 + +```cpp +/** + * Created by Liam Huang (Liam0205) on 2018/10/26. + */ + +#ifndef BSEARCH_BSEARCH_VARIENTS_HPP_ +#define BSEARCH_BSEARCH_VARIENTS_HPP_ + +#include +#include + +enum class BsearchPolicy { UNSPECIFIED, FIRST, LAST, FIRST_NOT_LESS, LAST_NOT_GREATER }; + +// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement, +// but with a bad time complexity. For better performance, iterators should meet +// the RandomAccessIterator requirement. +template ::value_type, + typename Compare> +IterT bsearch(IterT first, + IterT last, + ValueT target, + Compare comp, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + IterT result = last; + while (std::distance(first, last) > 0) { + IterT mid = first + std::distance(first, last) / 2; + if (policy == BsearchPolicy::FIRST_NOT_LESS) { + if (!comp(*mid, target)) { + if (mid == first or comp(*(mid - 1), target)) { + result = mid; + break; + } else { + last = mid; + } + } else { + first = mid + 1; + } + } else if (policy == BsearchPolicy::LAST_NOT_GREATER) { + if (comp(target, *mid)) { + last = mid; + } else { + if (std::distance(mid, last) == 1 or comp(target, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } + } else { // policy == UNSPECIFIED or FIRST or LAST + if (comp(*mid, target)) { + first = mid + 1; + } else if (comp(target, *mid)) { + last = mid; + } else { // equal + if (policy == BsearchPolicy::FIRST) { + if (mid == first or comp(*(mid - 1), *mid)) { + result = mid; + break; + } else { + last = mid; + } + } else if (policy == BsearchPolicy::LAST) { + if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) { + result = mid; + break; + } else { + first = mid + 1; + } + } else { + result = mid; + break; + } + } + } + } + return result; +} + +template ::value_type, + typename Compare = std::less> +IterT bsearch(IterT first, + IterT last, + ValueT target, + BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) { + return bsearch(first, last, target, Compare(), policy); +} + +#endif // BSEARCH_BSEARCH_VARIENTS_HPP_ +``` diff --git a/notes/17_skiplist/.gitkeep b/notes/17_skiplist/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/17_skiplist/readme.md b/notes/17_skiplist/readme.md new file mode 100644 index 00000000..9d9f87ac --- /dev/null +++ b/notes/17_skiplist/readme.md @@ -0,0 +1,43 @@ +# 跳表(Skip List) + +支持快速地: + +* 插入 +* 删除 +* 查找 + +某些情况下,跳表甚至可以替代红黑树(Red-Black tree)。Redis 当中的有序集合(Sorted Set)是用跳表实现的。 + +## 跳表的结构 + +跳表是对链表的改进。对于单链表来说,即使内容是有序的,查找具体某个元素的时间复杂度也要达到 $O(n)$。对于二分查找来说,由于链表不支持随机访问,根据 `first` 和 `last` 确定 `cut` 时,必须沿着链表依次迭代 `std::distance(first, last) / 2` 步;特别地,计算 `std::(first, last)` 本身,就必须沿着链表迭代才行。此时,二分查找的效率甚至退化到了 $O(n \log n)$,甚至还不如顺序遍历。 + +![单链表查找示例](https://static001.geekbang.org/resource/image/e1/6d/e18303fcedc068e5a168de04df956f6d.jpg) + +跳表的核心思想是用空间换时间,构建足够多级数的索引,来缩短查找具体值的时间开销。 + +![具有二级索引的跳表示例](https://static001.geekbang.org/resource/image/49/65/492206afe5e2fef9f683c7cff83afa65.jpg) + +例如对于一个具有 64 个有序元素的五级跳表,查找起来的过程大约如下图所示。 + +![五级跳表示例](https://static001.geekbang.org/resource/image/46/a9/46d283cd82c987153b3fe0c76dfba8a9.jpg) + +## 复杂度分析 + +对于一个每一级索引的跨度是下一级索引 $k$ 倍的跳表,每一次 `down` 操作,相当于将搜索范围缩小到「剩余的可能性的 $1 / k$」。因此,查找具体某个元素的时间复杂度大约需要 $\lfloor \log_k n\rfloor + 1$ 次操作;也就是说时间复杂度是 $O(\log n)$。 + +![跳表查询过程示例](https://static001.geekbang.org/resource/image/d0/0c/d03bef9a64a0368e6a0d23ace8bd450c.jpg) + +前面说了,跳表是一种用空间换时间的数据结构。因此它的空间复杂度一定不小。我们考虑原链表有 $n$ 个元素,那么第一级索引就有 $n / k$ 个元素,剩余的索引依次有 $n / k^2$, $n / k^3$, ..., $1$ 个元素。总共的元素个数是一个等比数列求和问题,它的值是 $\frac{n - 1}{k - 1}$。可见,不论 $k$ 是多少,跳表的空间复杂度都是 $O(n)$;但随着 $k$ 的增加,实际需要的额外节点数会下降。 + +## 高效地插入和删除 + +对于链表来说,插入或删除一个给定结点的时间复杂度是 $O(1)$。因此,对于跳表来说,插入或删除某个结点,其时间复杂度完全依赖于查找这类结点的耗时。而我们知道,在跳表中查找某个元素的时间复杂度是 $O(\log n)$。因此,在跳表中插入或删除某个结点的时间复杂度是 $O(\log n)$。 + +![在跳表中插入一个元素](https://static001.geekbang.org/resource/image/65/6c/65379f0651bc3a7cfd13ab8694c4d26c.jpg) + +## 跳表索引的动态更新 + +为了维护跳表的结构,在不断插入数据的过程中,有必要动态维护跳表的索引结构。一般来说,可以采用随机层级法。具体来说是引入一个输出整数的随机函数。当随机函数输出 $K$,则更新从第 $1$ 级至第 $K$ 级的索引。为了保证索引结构和数据规模大小的匹配,一般采用二项分布的随机函数。 + +![在跳表中插入一个元素并更新索引](https://static001.geekbang.org/resource/image/a8/a7/a861445d0b53fc842f38919365b004a7.jpg) diff --git a/notes/18_hashtable/.gitkeep b/notes/18_hashtable/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/18_hashtable/readme.md b/notes/18_hashtable/readme.md new file mode 100644 index 00000000..f841b107 --- /dev/null +++ b/notes/18_hashtable/readme.md @@ -0,0 +1,69 @@ +# 散列表 + +散列表是数组的一种扩展,利用数组下标的随机访问特性。 + +## 散列思想 + +* 键/关键字/Key:用来标识一个数据 +* 散列函数/哈希函数/Hash:将 Key 映射到数组下标的函数 +* 散列值/哈希值:Key 经过散列函数得到的数值 + +![](https://static001.geekbang.org/resource/image/92/73/92c89a57e21f49d2f14f4424343a2773.jpg) + +本质:利用散列函数将关键字映射到数组下标,而后利用数组随机访问时间复杂度为 $\Theta(1)$ 的特性快速访问。 + +## 散列函数 + +* 形式:`hash(key)` +* 基本要求 + 1. 散列值是非负整数 + 1. 如果 `key1 == key2`,那么 `hash(key1) == hash(key2)` + 1. 如果 `key1 != key2`,那么 `hash(key1) != hash(key2)` + +第 3 个要求,实际上不可能对任意的 `key1` 和 `key2` 都成立。因为通常散列函数的输出范围有限而输入范围无限。 + +## 散列冲突 + +* 散列冲突:`key1 != key2` 但 `hash(key1) == hash(key2)` + +散列冲突会导致不同键值映射到散列表的同一个位置。为此,我们需要解决散列冲突带来的问题。 + +### 开放寻址法 + +如果遇到冲突,那就继续寻找下一个空闲的槽位。 + +#### 线性探测 + +插入时,如果遇到冲突,那就依次往下寻找下一个空闲的槽位。(橙色表示已被占用的槽位,黄色表示空闲槽位) + +![](https://static001.geekbang.org/resource/image/5c/d5/5c31a3127cbc00f0c63409bbe1fbd0d5.jpg) + +查找时,如果目标槽位上不是目标数据,则依次往下寻找;直至遇见目标数据或空槽位。 + +![](https://static001.geekbang.org/resource/image/91/ff/9126b0d33476777e7371b96e676e90ff.jpg) + +删除时,标记为 `deleted`,而不是直接删除。 + +#### 平方探测(Quadratic probing) + +插入时,如果遇到冲突,那就往后寻找下一个空闲的槽位,其步长为 $1^2$, $2^2$, $3^2$, $\ldots$。 + +查找时,如果目标槽位上不是目标数据,则依次往下寻找,其步长为 $1^2$, $2^2$, $3^2$, $\ldots$;直至遇见目标数据或空槽位。 + +删除时,标记为 `deleted`,而不是直接删除。 + +#### 装载因子(load factor) + +$\text{load factor} = \frac{size()}{capacity()}$ + +### 链表法 + +所有散列值相同的 key 以链表的形式存储在同一个槽位中。 + +![](https://static001.geekbang.org/resource/image/a4/7f/a4b77d593e4cb76acb2b0689294ec17f.jpg) + +插入时,不论是否有冲突,直接插入目标位置的链表。 + +查找时,遍历目标位置的链表来查询。 + +删除时,遍历目标位置的链表来删除。 diff --git a/notes/19_hashtable/.gitkeep b/notes/19_hashtable/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/19_hashtable/readme.md b/notes/19_hashtable/readme.md new file mode 100644 index 00000000..7f985bf3 --- /dev/null +++ b/notes/19_hashtable/readme.md @@ -0,0 +1,59 @@ +# 散列表 + +核心:散列表的效率并不总是 $O(1)$,仅仅是在理论上能达到 $O(1)$。实际情况中,恶意攻击者可以通过精心构造数据,使得散列表的性能急剧下降。 + +如何设计一个工业级的散列表? + +## 散列函数 + +* 不能过于复杂——避免散列过程耗时 +* 散列函数的结果要尽可能均匀——最小化散列冲突 + +## 装载因子过大怎么办 + +动态扩容。涉及到 rehash,效率可能很低。 + +![](https://static001.geekbang.org/resource/image/67/43/67d12e07a7d673a9c1d14354ad029443.jpg) + +如何避免低效扩容? + +——将 rehash 的步骤,均摊到每一次插入中去: + +* 申请新的空间 +* 不立即使用 +* 每次来了新的数据,往新表插入数据 +* 同时,取出旧表的一个数据,插入新表 + +![](https://static001.geekbang.org/resource/image/6d/cb/6d6736f986ec4b75dabc5472965fb9cb.jpg) + +## 解决冲突 + +开放寻址法,优点: + +* 不需要额外空间 +* 有效利用 CPU 缓存 +* 方便序列化 + +开放寻址法,缺点: + +* 查找、删除数据时,涉及到 `delete` 标志,相对麻烦 +* 冲突的代价更高 +* 对装载因子敏感 + +链表法,优点: + +* 内存利用率较高——链表的优点 +* 对装载因子不敏感 + +链表法,缺点: + +* 需要额外的空间(保存指针) +* 对 CPU 缓存不友好 + +——将链表改造成更高效的数据结构,例如跳表、红黑树 + +## 举个栗子(JAVA 中的 HashMap) + +* 初始大小:16 +* 装载因子:超过 0.75 时动态扩容 +* 散列冲突:优化版的链表法(当槽位冲突元素超过 8 时使用红黑树,否则使用链表) diff --git a/notes/20_hashtable/.gitkeep b/notes/20_hashtable/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notes/20_hashtable/readme.md b/notes/20_hashtable/readme.md new file mode 100644 index 00000000..35580395 --- /dev/null +++ b/notes/20_hashtable/readme.md @@ -0,0 +1,35 @@ +# 散列表 + +散列表和链表的组合?为什么呢? + +* 链表:涉及查找的操作慢,不连续存储; +* 顺序表:支持随机访问,连续存储。 + +散列表 + 链表:结合优点、规避缺点。 + +## 结合散列表的 LRU 缓存淘汰算法 + +缓存的操作接口: + +* 向缓存添加数据 +* 从缓存删除数据 +* 在缓存中查找数据 + +然而——不管是添加还是删除,都涉及到查找数据。因此,单纯的链表效率低下。 + +魔改一把! + +![](https://static001.geekbang.org/resource/image/ea/6e/eaefd5f4028cc7d4cfbb56b24ce8ae6e.jpg) + +* `prev` 和 `next`:双向链表——LRU 的链表 +* `hnext`:单向链表——解决散列冲突的链表 + +操作: + +* 在缓存中查找数据:利用散列表 +* 从缓存中删除数据:先利用散列表寻找数据,然后删除——改链表就好了,效率很高 +* 向缓存中添加数据:先利用散列表寻找数据,如果找到了,LRU 更新;如果没找到,直接添加在 LRU 链表尾部 + +## Java: LinkedHashMap + +遍历时,按照访问顺序遍历。实现结构,与上述 LRU 的结构完全相同——只不过它不是缓存,不限制容量大小。 diff --git a/object-c/11_Sort/Sort.h b/object-c/11_Sort/Sort.h new file mode 100644 index 00000000..a962a62b --- /dev/null +++ b/object-c/11_Sort/Sort.h @@ -0,0 +1,24 @@ +// +// Sort.h +// test1231231 +// +// Created by Scarlett Che on 2018/12/12. +// Copyright © 2018 Scarlett Che. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface Sort : NSObject +// 冒泡排序 ++ (NSArray *)bubbleSortWithArray:(NSArray *)array; + +// 插入排序 ++ (NSArray *)insertionSortWithArray:(NSArray *)array; + +// 选择排序 ++ (NSArray *)selectionSortWithArray:(NSArray *)array; +@end + +NS_ASSUME_NONNULL_END diff --git a/object-c/11_Sort/Sort.m b/object-c/11_Sort/Sort.m new file mode 100644 index 00000000..d72bb872 --- /dev/null +++ b/object-c/11_Sort/Sort.m @@ -0,0 +1,86 @@ +// +// Sort.m +// test1231231 +// +// Created by Scarlett Che on 2018/12/12. +// Copyright © 2018 Scarlett Che. All rights reserved. +// + +#import "Sort.h" + +@implementation Sort +// 冒泡排序 ++ (NSArray *)bubbleSortWithArray:(NSArray *)array { + if (array.count <= 1) { + return array; + } + + NSMutableArray *aryM = array.mutableCopy; + + for (int i = 0; i < aryM.count - 1; i++) { + BOOL flag = NO; // 提前结束标记 + for (int j = 0; j < aryM.count - i - 1; j++) { + NSInteger value1 = [aryM[j] integerValue]; + NSInteger value2 = [aryM[j + 1] integerValue]; + + if (value1 > value2) { + flag = YES; + [aryM exchangeObjectAtIndex:j withObjectAtIndex:j+1]; + } + } + + + if (flag == NO) { + // 提前结束 + break; + } + } + return aryM.copy; +} + +// 插入排序 ++ (NSArray *)insertionSortWithArray:(NSArray *)array { + NSMutableArray *aryU = array.mutableCopy; + + for (int i = 1; i < aryU.count; i++) { + NSInteger value = [aryU[i] integerValue]; + + for (int j = 0; j < i; j ++) { + NSInteger sortedValue = [aryU[j] integerValue]; + if (value < sortedValue) { + id obj = aryU[i]; + [aryU removeObjectAtIndex:i]; + [aryU insertObject:obj atIndex:j]; + break; + } + } + } + return aryU.copy; +} + +// 选择排序 ++ (NSArray *)selectionSortWithArray:(NSArray *)array { + if (array.count <= 1) { + return array; + } + + NSMutableArray *aryM = array.mutableCopy; + for (int i = 0; i < array.count - 1; i++) { + NSInteger minIndex = NSNotFound; + NSInteger minValue = NSNotFound; + for (int j = i + 1; j < array.count - 1; j++) { + NSInteger tmp = [array[j] integerValue]; + if (tmp < minValue) { + minValue = tmp; + minIndex = j; + } + } + + if (minIndex != NSNotFound && minValue != NSNotFound && minValue < [array[i] integerValue]) { + [aryM exchangeObjectAtIndex:minIndex withObjectAtIndex:i]; + } + + } + return array; +} +@end diff --git a/object-c/33_bm_match/BM.h b/object-c/33_bm_match/BM.h new file mode 100644 index 00000000..0b1f9f49 --- /dev/null +++ b/object-c/33_bm_match/BM.h @@ -0,0 +1,19 @@ +// +// BM.h +// BM-Match +// +// Created by Smallfly on 2018/12/9. +// Copyright © 2018 Smallfly. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BM : NSObject +- (instancetype)initWithA:(NSString *)a andB:(NSString *)b; +- (NSInteger)startMatch; +- (void)startMatchCompeletion:(void (^)(NSInteger))completion; +@end + +NS_ASSUME_NONNULL_END diff --git a/object-c/33_bm_match/BM.m b/object-c/33_bm_match/BM.m new file mode 100644 index 00000000..4c42e62b --- /dev/null +++ b/object-c/33_bm_match/BM.m @@ -0,0 +1,131 @@ +// +// BM.m +// BM-Match +// +// Created by Smallfly on 2018/12/9. +// Copyright © 2018 Smallfly. All rights reserved. +// + +#import "BM.h" + +#define SIZE 256 + +@interface BM() +@property (nonatomic, strong) NSString *a; // 主串 +@property (nonatomic, strong) NSString *b; // 匹配串 + +@property (nonatomic, strong) NSMutableArray *bc; // 匹配串,哈希表,存储字符在匹配串中的下标 + +@property (nonatomic, strong) NSMutableArray *suffix; +@property (nonatomic, strong) NSMutableArray *prifix; + +@end + +@implementation BM + +- (instancetype)initWithA:(NSString *)a andB:(NSString *)b { + self = [super init]; + if (!self) return nil; + _a = a; + _b = b; + _bc = [NSMutableArray new]; + _suffix = [NSMutableArray new]; + _prifix = [NSMutableArray new]; + [self generateBC]; + [self generateGS]; + return self; +} + +// 构建坏字符哈希表,记录每个字符在匹配串中最后出现的位置 +- (void)generateBC { + for (int i = 0; i < SIZE; ++i) { + [_bc addObject:@-1]; + } + for (int i = 0; i < _b.length; ++i) { + int ascii = (int)[_b characterAtIndex:i]; // char to ASCII + _bc[ascii] = [NSNumber numberWithInteger:i]; // save b's char index + } +} + +- (NSInteger)bm { + NSInteger i = 0; // 主串和匹配串对齐的第一个字符 + NSUInteger n = _a.length; + NSUInteger m = _b.length; + while (i <= n - m) { + NSInteger j; + // 从后往前匹配 + for (j = m - 1; j >= 0; --j) { + int aValue = (int)[_a characterAtIndex:(i + j)]; + int bValue = (int)[_b characterAtIndex:j]; + if (aValue != bValue) break; // 找到坏字符下标 j 停止 + } + if (j < 0) { + return i; // 匹配成功,返回所在的位置 + } + + // 坏字符在匹配串中最后出现的位置 + id numberInHashTableBC = _bc[(int)[_a characterAtIndex:(i + j)]]; + NSInteger x = j - [numberInHashTableBC integerValue]; + NSInteger y = 0; + if (j < m - 1) { + y = [self moveByGSBy:j]; + } + i = i + MAX(x, y); + + // 这一步比较难理解,不直接滑到过 j,是因为在 j 之前可能存在于坏字符相同的字符 + // 这个于坏字符相同的字符,在匹配串中的最大下标是 numberInHashTableBC +// i = i + (j - [numberInHashTableBC integerValue]); + } + + return -1; +} + +// 好后缀匹配移动 +- (NSInteger)moveByGSBy:(NSInteger)j { + NSUInteger m = _b.length; + NSInteger k = m - 1 - j; // 好后缀的长度 + NSInteger t = [_suffix[k] integerValue]; + if (t != -1) return j - t + 1; // 匹配串的前缀,是否匹配好后缀,关键 + for (NSInteger r = j+1; r <= m-1; ++r) { + if ([_prifix[m-r] boolValue]) { // 关键 + return r; + } + } + return m; +} + +- (void)generateGS { + NSUInteger m = _b.length; + for (NSInteger i = 0; i < m; ++i) { + _prifix[i] = @(NO); + _suffix[i] = @(-1); + } + for (NSInteger i = 0; i < m - 1; ++i) { // 从 b 中取两个字符对比 + NSInteger j = i; + NSInteger k = 0; // 公共后缀的长度 + int jValue = (int)[_b characterAtIndex:j]; + int bmValue = (int)[_b characterAtIndex:(m-1-k)]; + while (j >= 0 && jValue == bmValue) { // 与 b[0, m-1] 求公共子串 + ++k; + --j; + _suffix[k] = [NSNumber numberWithInteger:(j+1)]; //j+1 代表公共子串在 b 中的起始下标 + } + if (j == -1) _prifix[k] = @(YES); + } +} + +#pragma mark - + +// 同步 +- (NSInteger)startMatch { + return [self bm]; +} + +// 异步 +- (void)startMatchCompeletion:(void (^)(NSInteger))completion { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + completion([self bm]); + }); +} + +@end diff --git a/object-c/33_bm_match/main.m b/object-c/33_bm_match/main.m new file mode 100644 index 00000000..ebddc71a --- /dev/null +++ b/object-c/33_bm_match/main.m @@ -0,0 +1,23 @@ +// +// main.m +// BM-Match +// +// Created by Smallfly on 2018/12/9. +// Copyright © 2018 Smallfly. All rights reserved. +// + +#import +#import "BM.h" + +int main(int argc, const char * argv[]) { + @autoreleasepool { + BM *bm = [[BM alloc] initWithA:@"abacadc" andB:@"adc"]; + + [bm startMatchCompeletion:^(NSInteger index) { + NSLog(@"异步查找到下标:%ld\n", index); + }]; + + NSLog(@"同步查找到下标:%ld\n", [bm startMatch]); + } + return 0; +} diff --git a/php/.gitignore b/php/.gitignore index 3ce5adbb..9426df25 100644 --- a/php/.gitignore +++ b/php/.gitignore @@ -1,2 +1,4 @@ .idea vendor +*my* +Queue \ No newline at end of file diff --git a/php/05_array/array.php b/php/05_array/array.php index a312f45c..f4df47e5 100644 --- a/php/05_array/array.php +++ b/php/05_array/array.php @@ -52,7 +52,7 @@ public function checkIfFull() */ private function checkOutOfRange($index) { - if($index > $this->length+1) { + if($index >= $this->length) { return true; } return false; @@ -68,18 +68,16 @@ public function insert($index, $value) { $index = intval($index); $value = intval($value); - if($index < 0) { + if ($index < 0) { return 1; } - if($this->checkIfFull()) { + + if ($this->checkIfFull()) { return 2; } - if($this->checkOutOfRange($index)) { - return 3; - } - for($i=$this->length-1;$i>=$index;$i--) { - $this->data[$i+1] = $this->data[$i]; + for ($i = $this->length - 1; $i >= $index; $i--) { + $this->data[$i + 1] = $this->data[$i]; } $this->data[$index] = $value; @@ -96,22 +94,21 @@ public function delete($index) { $value = 0; $index = intval($index); - if($index < 0) { + if ($index < 0) { $code = 1; - return array($code, $value); + return [$code, $value]; } - if($index != $this->length+1 && $this->checkOutOfRange($index)) { + if ($this->checkOutOfRange($index)) { $code = 2; - return array($code, $value); + return [$code, $value]; } $value = $this->data[$index]; - for($i=$index;$i<$this->length-1;$i++) { - $this->data[$i] = $this->data[$i+1]; + for ($i = $index; $i < $this->length - 1; $i++) { + $this->data[$i] = $this->data[$i + 1]; } - $this->length--; - return array(0, $value); + return [0, $value]; } /** @@ -123,23 +120,23 @@ public function find($index) { $value = 0; $index = intval($index); - if($index < 0) { + if ($index < 0) { $code = 1; - return array($code, $value); + return [$code, $value]; } - if($this->checkOutOfRange($index)) { + if ($this->checkOutOfRange($index)) { $code = 2; - return array($code, $value); + return [$code, $value]; } - return array(0, $this->data[$index]); + return [0, $this->data[$index]]; } public function printData() { $format = ""; - for($i=0;$i<$this->length;$i++) { - $format .= "|".$this->data[$i]; + for ($i = 0; $i < $this->length; $i++) { + $format .= "|" . $this->data[$i]; } - print($format."\n"); + print($format . "\n"); } -} \ No newline at end of file +} diff --git a/php/06_linkedlist/SingleLinkedList.php b/php/06_linkedlist/SingleLinkedList.php index 59288601..222f638a 100644 --- a/php/06_linkedlist/SingleLinkedList.php +++ b/php/06_linkedlist/SingleLinkedList.php @@ -133,7 +133,7 @@ public function getPreNode(SingleLinkedListNode $node) $preNode = $this->head; // 遍历找到前置节点 要用全等判断是否是同一个对象 // http://php.net/manual/zh/language.oop5.object-comparison.php - while ($curNode !== $node) { + while ($curNode !== $node && $curNode != null) { $preNode = $curNode; $curNode = $curNode->next; } diff --git a/php/07_linkedlist/main.php b/php/07_linkedlist/main.php index 736b0486..4cd9b315 100644 --- a/php/07_linkedlist/main.php +++ b/php/07_linkedlist/main.php @@ -191,7 +191,7 @@ public function deleteLastKth($index) ++$i; } - if ($fast == null) { + if (null == $fast) { return true; } @@ -305,4 +305,4 @@ public function findMiddleNode() $listAlgo->setList($list); $middleNode = $listAlgo->findMiddleNode(); var_dump($middleNode->data); -echo '-------------------------------------------------------------'. PHP_EOL . PHP_EOL; \ No newline at end of file +echo '-------------------------------------------------------------'. PHP_EOL . PHP_EOL; diff --git a/php/08_stack/.gitkeep b/php/08_stack/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/php/08_stack/Compute.php b/php/08_stack/Compute.php new file mode 100644 index 00000000..5bd2913b --- /dev/null +++ b/php/08_stack/Compute.php @@ -0,0 +1,88 @@ += 48 && ord($arr[$i]) <= 57){ + array_push($numStack, $arr[$i]); + continue; + } + switch ($arr[$i]){ + case '+': + case '-': + $arrLen = count($operStack); + while ($operStack[$arrLen-1] === '*' || $operStack[$arrLen-1] === '/' || $operStack[$arrLen-1] === '-'){ + compute($numStack, $operStack); + $arrLen--; + } + array_push($operStack, $arr[$i]); + break; + case '*': + $arrLen = count($operStack); + while ($operStack[$arrLen-1] === '/'){ + compute($numStack, $operStack); + $arrLen--; + } + array_push($operStack, $arr[$i]); + break; + + case '/': + case '(': + array_push($operStack, $arr[$i]); + break; + case ')': + $arrLen = count($operStack); + while ($operStack[$arrLen-1] !== '('){ + compute($numStack, $operStack); + $arrLen--; + } + array_pop($operStack); + break; + default: + throw new \Exception("不支持的运算符", 1); + break; + } + } + + $arrLen = count($operStack); + while ($operStack[$arrLen-1] !== NULL){ + compute($numStack, $operStack); + $arrLen--; + } + echo array_pop($numStack); +} + +//数字栈长度减一,运算符栈长度减一 +function compute(&$numStack, &$operStack){ + $num = array_pop($numStack); + switch (array_pop($operStack)) { + case '*': + array_push($numStack, array_pop($numStack) * $num); + break; + case '/': + array_push($numStack, array_pop($numStack) / $num); + break; + case '+': + array_push($numStack, array_pop($numStack) + $num); + break; + case '-': + array_push($numStack, array_pop($numStack) - $num); + break; + case '(': + throw new \Exception("不匹配的(", 2); + break; + } +} +expression('-1+2-(1+2*3)'); +echo PHP_EOL; +eval('echo -1+2-(1+2*3);'); diff --git a/php/08_stack/StackOnLinkedList.php b/php/08_stack/StackOnLinkedList.php new file mode 100644 index 00000000..bfac655d --- /dev/null +++ b/php/08_stack/StackOnLinkedList.php @@ -0,0 +1,159 @@ +head = new SingleLinkedListNode(); + $this->length = 0; + } + + /** + * 出栈 + * + * @return bool + */ + public function pop() + { + if (0 == $this->length) { + return false; + } + + $this->head->next = $this->head->next->next; + $this->length--; + + return true; + } + + /** + * 入栈 + * + * @param $data + * + * @return SingleLinkedListNode|bool + */ + public function push($data) + { + return $this->pushData($data); + } + + /** + * 入栈 node + * + * @param SingleLinkedListNode $node + * + * @return bool + */ + public function pushNode(SingleLinkedListNode $node) + { + if (null == $node) { + return false; + } + + $node->next = $this->head->next; + $this->head->next = $node; + + $this->length++; + return true; + } + + /** + * 入栈 data + * + * @param $data + * + * @return SingleLinkedListNode|bool + */ + public function pushData($data) + { + $node = new SingleLinkedListNode($data); + + if (!$this->pushNode($node)) { + return false; + } + + return $node; + } + + /** + * 获取栈顶元素 + * + * @return SingleLinkedListNode|bool|null + */ + public function top() + { + if (0 == $this->length) { + return false; + } + + return $this->head->next; + } + + /** + * 打印栈 + */ + public function printSelf() + { + if (0 == $this->length) { + echo 'empty stack' . PHP_EOL; + return; + } + + echo 'head.next -> '; + $curNode = $this->head; + while ($curNode->next) { + echo $curNode->next->data . ' -> '; + + $curNode = $curNode->next; + } + echo 'NULL' . PHP_EOL; + } + + /** + * 获取栈长度 + * + * @return int + */ + public function getLength() + { + return $this->length; + } + + /** + * 判断栈是否为空 + * + * @return bool + */ + public function isEmpty() + { + return $this->length > 0 ? false : true; + } +} \ No newline at end of file diff --git a/php/08_stack/main.php b/php/08_stack/main.php new file mode 100644 index 00000000..63478217 --- /dev/null +++ b/php/08_stack/main.php @@ -0,0 +1,32 @@ +pushData(1); +$stack->pushData(2); +$stack->pushData(3); +$stack->pushData(4); +var_dump($stack->getLength()); +$stack->printSelf(); + +$topNode = $stack->top(); +var_dump($topNode->data); + +$stack->pop(); +$stack->printSelf(); +$stack->pop(); +$stack->printSelf(); + +var_dump($stack->getLength()); + +$stack->pop(); +$stack->pop(); +$stack->printSelf(); \ No newline at end of file diff --git a/php/09_queue/QueueOnLinkedList.php b/php/09_queue/QueueOnLinkedList.php new file mode 100644 index 00000000..7954b5c1 --- /dev/null +++ b/php/09_queue/QueueOnLinkedList.php @@ -0,0 +1,115 @@ +head = new SingleLinkedListNode(); + $this->tail = $this->head; + + $this->length = 0; + } + + /** + * 入队 + * + * @param $data + */ + public function enqueue($data) + { + $newNode = new SingleLinkedListNode(); + $newNode->data = $data; + + $this->tail->next = $newNode; + $this->tail = $newNode; + + $this->length++; + } + + /** + * 出队 + * + * @return SingleLinkedListNode|bool|null + */ + public function dequeue() + { + if (0 == $this->length) { + return false; + } + + $node = $this->head->next; + $this->head->next = $this->head->next->next; + + $this->length--; + + return $node; + } + + /** + * 获取队列长度 + * + * @return int + */ + public function getLength() + { + return $this->length; + } + + /** + * 打印队列 + */ + public function printSelf() + { + if (0 == $this->length) { + echo 'empty queue' . PHP_EOL; + return; + } + + echo 'head.next -> '; + $curNode = $this->head; + while ($curNode->next) { + echo $curNode->next->data . ' -> '; + + $curNode = $curNode->next; + } + echo 'NULL' . PHP_EOL; + } +} \ No newline at end of file diff --git a/php/09_queue/Sequential.php b/php/09_queue/Sequential.php new file mode 100644 index 00000000..b5d023e3 --- /dev/null +++ b/php/09_queue/Sequential.php @@ -0,0 +1,60 @@ +MaxSzie = ++$size; + } + + /** + * 队列满条件 ($this->tail+1) % $this->MaxSzie == $this->head + */ + public function enQueue($data) + { + if (($this->tail+1) % $this->MaxSzie == $this->head) + return -1; + + $this->data[$this->tail] = $data; + $this->tail = (++$this->tail) % $this->MaxSzie; + } + + public function deQueue() + { + if ($this->head == $this->tail) + return NULL; + + $data = $this->data[$this->head]; + unset($this->data[$this->head]); + $this->head = (++$this->head) % $this->MaxSzie; + return $data; + } + + public function getLength() + { + return ($this->tail - $this->head + $this->MaxSzie) % $this->MaxSzie; + } +} + +$queue = new LoopQueue(4); +// var_dump($queue); +$queue->enQueue(1); +$queue->enQueue(2); +$queue->enQueue(3); +$queue->enQueue(4); +// $queue->enQueue(5); +var_dump($queue->getLength()); +$queue->deQueue(); +$queue->deQueue(); +$queue->deQueue(); +$queue->deQueue(); +$queue->deQueue(); +var_dump($queue); diff --git a/php/09_queue/main.php b/php/09_queue/main.php new file mode 100644 index 00000000..1f49e62f --- /dev/null +++ b/php/09_queue/main.php @@ -0,0 +1,29 @@ +enqueue(1); +$queue->enqueue(2); +$queue->enqueue(3); +$queue->enqueue(4); +$queue->enqueue(5); +$queue->printSelf(); +var_dump($queue->getLength()); + +$queue->dequeue(); +$queue->printSelf(); +$queue->dequeue(); +$queue->dequeue(); +$queue->dequeue(); +$queue->printSelf(); + +$queue->dequeue(); +$queue->printSelf(); diff --git a/php/11_sort/Sort.php b/php/11_sort/Sort.php new file mode 100644 index 00000000..cbee5ceb --- /dev/null +++ b/php/11_sort/Sort.php @@ -0,0 +1,67 @@ + $arr[$j + 1]) { + $tmp = $arr[$j]; + $arr[$j] = $arr[$j + 1]; + $arr[$j + 1] = $tmp; + $flag = true; + } + } + if (!$flag) { + break; + } + } +} + +// 插入排序 +function insertionSort(&$arr) +{ + $n = count($arr); + if ($n <= 1) return; + + for ($i = 1; $i < $n; ++$i) { + $value = $arr[$i]; + $j = $i - 1; + // 查找插入的位置 + for (; $j >= 0; --$j) { + if ($arr[$j] > $value) { + $arr[$j + 1] = $arr[$j]; // 数据移动 + } else { + break; + } + } + $arr[$j + 1] = $value; // 插入数据 + } +} + +// 选择排序 +function selectionSort(&$arr) +{ + $length = count($arr); + if ($length <= 1) return; + + for ($i = 0; $i < $length - 1; $i++) { + //先假设最小的值的位置 + $p = $i; + for ($j = $i + 1; $j < $length; $j++) { + if ($arr[$p] > $arr[$j]) { + $p = $j; + } + } + $tmp = $arr[$p]; + $arr[$p] = $arr[$i]; + $arr[$i] = $tmp; + } +} + +$arr = [1,4,6,2,3,5,4]; +insertionSort($arr); +var_dump($arr); diff --git a/php/12_sort/mergeSort.php b/php/12_sort/mergeSort.php new file mode 100644 index 00000000..a4ce4e3b --- /dev/null +++ b/php/12_sort/mergeSort.php @@ -0,0 +1,73 @@ += $r) { + return [$arr[$r]]; + } + + // 取 p 到 r 之间的中间位置 q + $q = (int)(($p + $r) / 2); + + // 分治递归 + $left = mergeSortRecursive($arr, $p, $q); + $right = mergeSortRecursive($arr, $q + 1, $r); + return merge($left, $right); +} + +// 合并 +function merge(array $left, array $right) +{ + $tmp = []; + $i = $j = 0; + + $leftLength = count($left); + $rightLength = count($right); + + do { + if ($left[$i] <= $right[$j]) { + $tmp[] = $left[$i++]; + } else { + $tmp[] = $right[$j++]; + } + } while ($i < $leftLength && $j < $rightLength); + + $start = $i; + $end = $leftLength; + $copyArr = $left; + + // 判断哪个子数组中有剩余的数据 + if ($j < $rightLength) { + $start = $j; + $end = $rightLength; + $copyArr = $right; + } + + // 将剩余的数据拷贝到临时数组 tmp + do { + $tmp[] = $copyArr[$start++]; + } while ($start < $end); + + return $tmp; +} \ No newline at end of file diff --git a/php/12_sort/quicksort.php b/php/12_sort/quicksort.php new file mode 100644 index 00000000..95a60cae --- /dev/null +++ b/php/12_sort/quicksort.php @@ -0,0 +1,47 @@ += $r) return; + + $q = partition($a, $l, $r); + quickSortInternally($a, $l, $q-1); + quickSortInternally($a, $q+1, $r); +} + +function partition(&$a, $l, $r): int +{ + $pivot = $a[$r]; + $i = $l; + + for ($j = $l; $j < $r; ++$j) { + if ($a[$j] < $pivot) { + [$a[$j], $a[$i]] = [$a[$i], $a[$j]]; + ++$i; + } + } + + [$a[$r], $a[$i]] = [$a[$i], $a[$r]]; + + return $i; +} + +$a1 = [1,4,6,2,3,5,4]; +$a2 = [2, 2, 2, 2]; +$a3 = [4, 3, 2, 1]; +$a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]; +quickSort($a1); +print_r($a1); +quickSort($a2); +print_r($a2); +quickSort($a3); +print_r($a3); +quickSort($a4); +print_r($a4); \ No newline at end of file diff --git a/php/13_sort/bucketSort.php b/php/13_sort/bucketSort.php new file mode 100644 index 00000000..37b0dcc8 --- /dev/null +++ b/php/13_sort/bucketSort.php @@ -0,0 +1,50 @@ + $value) { + $index = ceil(($value-$min)/$length); + $buckets[$index][] = $value; + } + + $result = []; + for($i=0;$i<$bucketNumber;$i++) { + $bucket = $buckets[$i]; + $length = count($bucket); + //如果桶内元素为空,跳过这个桶 + if($length == 0) { + continue; + } + if( $length > 10) { + $bucket = bucketSort($bucket); + } + + quickSort($bucket,0,count($bucket)-1); + $result = array_merge($result,$bucket); + } + return $result; +} + diff --git a/php/13_sort/countingSort.php b/php/13_sort/countingSort.php new file mode 100644 index 00000000..edef0b89 --- /dev/null +++ b/php/13_sort/countingSort.php @@ -0,0 +1,43 @@ + $value) { + $countScore[$value]++; + } + + /** + * 顺序求和 + */ + for($i=1;$i<=5;$i++) { + $countScore[$i] += $countScore[$i-1]; + } + /** + * 排序 + */ + foreach ($score as $key => $value) { + $countScore[$value] --; + $temp[$countScore[$value]] = $value; + } + //copy + for($i=0;$i<$length;$i++) { + $score[$i] = $temp[$i]; + } + return $score; +} \ No newline at end of file diff --git a/php/13_sort/radixSort.php b/php/13_sort/radixSort.php new file mode 100644 index 00000000..3f4164be --- /dev/null +++ b/php/13_sort/radixSort.php @@ -0,0 +1,48 @@ +toArray(); + foreach ($numbers as $key => $value) { + $index = ($value/$divisor)%10;//计算该数字在哪个桶中 + $buckets[$index][] = $value; + } + /** + * 从桶中取出数字 + */ + $k=0; + for($i=0;$i<10;$i++) { + while(count($buckets[$i]) > 0) { + $numbers[$k++] = array_shift($buckets[$i]); + } + } +} diff --git a/php/15_binary/binary.php b/php/15_binary/binary.php new file mode 100644 index 00000000..26455d68 --- /dev/null +++ b/php/15_binary/binary.php @@ -0,0 +1,102 @@ + $high) { + return -1; + } + + /** + * notice2 mid计算 + */ + $mid = $low + (($high - $low) >> 1); + if ($numbers[$mid] > $find) { + //notice3 high值更新 + return search($numbers, $low, $mid -1, $find); + } elseif ($numbers[$mid] < $find) { + //notice4 low值更新 + return search($numbers, $mid + 1, $high, $find); + } else { + return $mid; + } +} + +/** + * 求数字的平方根,保留6位小数 + * @param [type] $number + * + * @return void + * @date 2018/11/26 + * @author yuanliandu + */ +function squareRoot($number) +{ + if ($number < 0) { + return -1; + } elseif ($number < 1) { + $min = $number; + $max = 1; + } else { + $min = 1; + $max = $number; + } + $mid = $min + ($max - $min) / 2; + while (getDecimalPlaces($mid) < 6) { + $square = $mid * $mid; + if ($square > $number) { + $max = $mid; + } elseif ($square == $number) { + return $mid; + } else { + $min = $mid; + } + $mid = $min + ($max - $min) / 2; + } + return $mid; +} + +/** + * 计算数字小数点后有几位数字 + * @param [type] $number + * + * @return void + * @date 2018/11/27 + * @author yuanliandu + */ +function getDecimalPlaces($number) +{ + $temp = explode('.', $number); + if (isset($temp[1])) { + return strlen($temp[1]); + } + + return 0; +} + +// 测试二分查找给定值 +$numbers = [0, 1, 2, 3, 3, 4, 5, 6, 7, 9]; +$find = 1; +var_dump(binarySearch($numbers,$find)); + +//测试求平方根 +var_dump(squareRoot(3)); \ No newline at end of file diff --git a/php/16_binary/binary.php b/php/16_binary/binary.php new file mode 100644 index 00000000..fc27a4cb --- /dev/null +++ b/php/16_binary/binary.php @@ -0,0 +1,191 @@ + + */ +function findFirstEqual(array $numbers,$find) { + $length = count($numbers); + $low = 0; + $high = $length - 1; + while($low <= $high) { + $mid = $low + (($high-$low)>>1); + if($numbers[$mid] > $find) { + $high = $mid - 1; + }else if($numbers[$mid] < $find) { + $low = $mid + 1; + }else { + /** + * 如果是第一个元素,或之前一个元素不等于我们要找的值 + * 我们就找到了第一个=find的element + */ + if($mid==0 || $numbers[$mid-1]!=$find) { + return $mid; + }else { + $high = $mid - 1; + } + } + } + + return -1; +} + +/** + * 找到最后一个=find的元素 + * @param array $numbers + * @param [type] $find + * + * @return void + * @date 2018/11/27 + * @author yuanliandu + */ +function findLastEqual(array $numbers,$find) { + $length = count($numbers); + $low = 0; + $high = $length - 1; + while($low <= $high) { + $mid = $low + (($high-$low)>>1); + if($numbers[$mid] > $find) { + $high = $mid - 1; + }else if($numbers[$mid] < $find) { + $low = $mid + 1; + }else { + /** + * 如果mid是最后一个元素的index + * 或mid后一个元素!=我们要找的值 + * 则找到了最后一个=find的value + */ + if($mid==$length-1 || $numbers[$mid+1]!=$find) { + return $mid; + }else { + $low = $mid + 1; + } + } + } + + return -1; +} + +/** + * 找到第一个大于等于find的元素 + * @param array $numbers + * @param [type] $find + * + * @return void + * @date 2018/11/27 + * @author yuanliandu + */ +function findFirstGreaterEqual(array $numbers,$find) { + $length = count($numbers); + $low = 0; + $high = $length - 1; + while($low <= $high) { + $mid = $low + (($high-$low)>>1); + if($numbers[$mid] >= $find) { + if ($mid == 0 || $numbers[$mid-1] < $find) { + return $mid; + }else { + $high = $mid - 1; + } + }else { + $low = $mid + 1; + } + } + return -1; +} + +/** + * 找到最后一个小于等于find的元素 + * @param array $numbers + * @param [type] $find + * + * @return void + * @date 2018/11/27 + * @author yuanliandu + */ +function findLastLessEqual(array $numbers,$find) { + $length = count($numbers); + $low = 0; + $high = $length - 1; + while($low <= $high) { + $mid = $low + (($high-$low)>>1); + if($numbers[$mid] <= $find) { + if($mid==$length-1 || $numbers[$mid+1]> $find) { + return $mid; + } + $low = $mid + 1; + }else { + $high = $mid - 1; + } + } + return -1; +} + + + + + +/** + * 循环数组中找指定元素 + * @param array $numbers + * @param [type] $find + * + * @return void + * @date 2018/11/27 + * @author yuanliandu + */ +function searchCircularArray(array $numbers,$find) { + $length = count($numbers); + $low = 0; + $high = $length - 1; + + while($low <= $high) { + $mid = $low + (($high-$low)>>1); + if($numbers[$mid] === $find) { + return $mid; + } + + if($numbers[$low] > $numbers[$mid]) { + // 后半部分是有序数组 + if(($numbers[$mid] < $find) && ($numbers[$high] >= $find)) { + if($numbers[$high] === $find) return $high; + //在后半个区间内 + $low = $mid + 1; + }else { + $high = $mid - 1; + } + }else { + // 前半部分是有序的 + if(($numbers[$low] <= $find) && ($numbers[$mid] > $find)) { + // 在有序区间内 + if($numbers[$low] === $find) return $low; + $high = $mid - 1; + }else { + $low = $mid + 1; + } + } + + } + return -1; +} + +/*** + * 测试 + */ +$numbers = [1,2,3,3,3,4,5,6,8,11,13]; +$find = 3; + +var_dump(findFirstEqual($numbers,$find));//找到第一个等于find的元素 +var_dump(findFirstGreaterEqual($numbers,$find));//找到第一个大于等于find的元素 +var_dump(findLastEqual($numbers,$find));//找到最后一个=find的元素 +var_dump(findLastLessEqual($numbers,$find));//找到最后一个小于等于find的元素 + + +//测试在循环数组中找到指定数字 +$numbers = [9,10,1,2,3,4,5,6,7,8]; +$find = 2; +var_dump(searchCircularArray($numbers,$find)); diff --git a/php/17_skiplist/skipList.php b/php/17_skiplist/skipList.php new file mode 100644 index 00000000..dc087172 --- /dev/null +++ b/php/17_skiplist/skipList.php @@ -0,0 +1,108 @@ +data = $data; + } + + //获取当前节点索引层数 + public function getMaxLevel() + { + return count($this->next) - 1; + } +} + +class SkipList +{ + //索引最大层数 + public $indexLevel; + + //头节点 + protected $head; + + public function __construct(int $indexLevel) + { + $this->indexLevel = max($indexLevel, 0); + $this->head = new SNode(); + } + + public function addData($data) + { + $newNode = new SNode($data); + for ($level = $this->getRandomLevel(), $node = $this->head; $level >= 0; $level--) { + while (isset($node->next[$level]) && $data < $node->next[$level]->data) { + $node = $node->next[$level]; + } + if (isset($node->next[$level])) { + $newNode->next[$level] = $node->next[$level]; + } + $node->next[$level] = $newNode; + } + return $newNode; + } + + public function deleteData($data) + { + $deleted = false; + for ($level = $this->head->getMaxLevel(), $node = $this->head; $level >= 0; $level--) { + while (isset($node->next[$level]) && $data < $node->next[$level]->data) { + $node = $node->next[$level]; + } + if (isset($node->next[$level]) && $data == $node->next[$level]->data) { + $node->next[$level] = isset($node->next[$level]->next[$level]) ? + $node->next[$level]->next[$level] : null; + $deleted = true; + } + } + return $deleted; + } + + public function findData($data) + { + for ($level = $this->head->getMaxLevel(), $node = $this->head; $level >= 0; $level--) { + while (isset($node->next[$level]) && $data < $node->next[$level]->data) { + $node = $node->next[$level]; + } + if (isset($node->next[$level]) && $data == $node->next[$level]->data) { + return $node->next[$level]; + } + } + return false; + } + + protected function getRandomLevel() + { + return mt_rand(0, $this->indexLevel); + } +} + +/** + * 示例 + */ + +$indexLevel = 2; + +$skipList = new SkipList($indexLevel); + +for ($i = 10; $i >= 0; $i--) { + $skipList->addData($i); +} + +//打印0到10组成的跳表 +var_dump($skipList); + +//返回SNode对象 +var_dump($skipList->findData(5)); + +$skipList->deleteData(5); + +//返回false +var_dump($skipList->findData(5)); \ No newline at end of file diff --git a/php/24_tree/Tree.php b/php/24_tree/Tree.php new file mode 100644 index 00000000..97fd14b7 --- /dev/null +++ b/php/24_tree/Tree.php @@ -0,0 +1,152 @@ +head = new TreeNode($headData); + } + } + + /** + * 查找数据 + * @param [type] $data [数据] + * @return [type] [description] + */ + public function find($data) + { + if ($this->head == null) { + return null; + } + + $node = $this->head; + + while ($node != null) { + if ($node->data == $data) { + return $node; + } elseif ($data > $node->data) { + $node = $node->right; + } else { + $node = $node->left; + } + } + + return null; + } + + /** + * 插入数据 + * @param [type] $data [数据] + * @return [type] [description] + */ + public function insert($data) + { + if ($this->head == null) { + $this->head = new TreeNode($data); + return true; + } + + $node = $this->head; + + while ($node != null) { + if ($data > $node->data) { + if ($node->right == null) { + $node->right = new TreeNode($data); + return true; + } + $node = $node->right; + } else { + if ($node->left == null) { + $node->left = new TreeNode($data); + return true; + } + $node = $node->left; + } + } + } + + /** + * 删除节点 + * @param [type] $data [节点] + * @return [type] [description] + */ + public function delete($data) + { + // 找到需要删除节点 + $node = $this->head; + $pnode = null; + while ($node != null) { + if ($node->data == $data) { + break; + } elseif ($data > $node->data) { + $pnode = $node; + $node = $node->right; + } else { + $pnode = $node; + $node = $node->left; + } + } + if ($node == null) { + return false; + } + // 要删除的节点有两个子节点 + // 查找右子树中最小节点 + if ($node->left != null && $node->right != null) { + $minPP = $node; + $minP = $node->right; + while ($minP->left != null) { + $minPP = $minP; + $minP = $minP->left; + } + $node->data = $minP->data; + $node = $minP; + // 删除掉右子树中的最小节点 + $minPP->left = null; + } + + if ($node->left != null) { + $child = $node->left; + } elseif ($node->right != null) { + $child = $node->right; + } else { + $child = null; + } + + if ($pnode == null) { + // 删除的是根节点 + $node = $child; + } elseif ($pnode->left == $node) { + $pnode->left = $child; + } else { + $pnode->right = $child; + } + } + + /** + * 前序遍历 + * @return [type] [description] + */ + public function preOrder($node) + { + if ($node == null) { + return ; + } + echo $node->data . '->'; + $this->preOrder($node->left); + $this->preOrder($node->right); + } +} \ No newline at end of file diff --git a/php/24_tree/TreeNode.php b/php/24_tree/TreeNode.php new file mode 100644 index 00000000..21c3e1a4 --- /dev/null +++ b/php/24_tree/TreeNode.php @@ -0,0 +1,36 @@ +data = $data; + $this->left = null; + $this->right = null; + } +} \ No newline at end of file diff --git a/php/24_tree/main.php b/php/24_tree/main.php new file mode 100644 index 00000000..f68ba9f3 --- /dev/null +++ b/php/24_tree/main.php @@ -0,0 +1,25 @@ +insert(20); +$tree->insert(30); +$tree->insert(10); +$tree->insert(21); +$tree->insert(22); + +$tree->preOrder($tree->head); +echo PHP_EOL; + +var_dump($tree->find(30)); +echo PHP_EOL; + + +$tree->delete(30); +$tree->preOrder($tree->head); +echo PHP_EOL; \ No newline at end of file diff --git a/php/38_divide_and_conquer/matrix_production.php b/php/38_divide_and_conquer/matrix_production.php new file mode 100644 index 00000000..dcf5b765 --- /dev/null +++ b/php/38_divide_and_conquer/matrix_production.php @@ -0,0 +1,77 @@ + &$row) { + $data = array_shift($row); + if (empty($data) || empty($row)) { + unset($matrix[$key]); + } + $column[] = [$data]; + } + return $column; +} + +function countProduction($row, $column) +{ + for ($i = 0, $sum = 0; $i < count($row[0]); $i++) { + $sum += $row[0][$i] * $column[$i][0]; + } + return $sum; +} + +function merger($value1, $value2, $value3, $value4) +{ + if (empty($value2) && empty($value3)) { + return $value1; + } else { + $array12 = array_merge([$value1], !is_array($value2) ? [$value2] : $value2); + if (!is_array($value3)) { + $array34 = array_merge([$value3], !is_array($value4) ? [$value4] : $value4); + return [$array12, $array34]; + } else { + for ($i = 0, $array34 = []; $i < count($value3); $i++) { + $array34[] = array_merge($value3[$i], $value4[$i]); + } + return array_merge([$array12], $array34); + } + } +} + +function matrixProduction($matrix1, $matrix2) +{ + $row = popRow($matrix1); + $column = popColumn($matrix2); + + if (empty($row) || empty($column)) { + return []; + } + + $value1 = countProduction($row, $column); + $value2 = matrixProduction($row, $matrix2); + $value3 = matrixProduction($matrix1, $column); + $value4 = matrixProduction($matrix1, $matrix2); + + return merger($value1, $value2, $value3, $value4); +} + +$matrix1 = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]; + +$matrix2 = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]; + +var_dump(matrixProduction($matrix1, $matrix2)); diff --git a/php/39_backtracking/queens.php b/php/39_backtracking/queens.php new file mode 100644 index 00000000..de99eef5 --- /dev/null +++ b/php/39_backtracking/queens.php @@ -0,0 +1,74 @@ +printQueens(); + return; + } + + //每一行有8中放法 + for($column = 0; $column < 8; $column++) { + if ($this->isOk($row, $column)) { + $this->result[$row] = $column; + $this->cal8queens($row + 1); + } + } + } + + //row行的column列是否合适 + function isOk($row, $column) + { + $leftup = $column - 1; + $rightdown = $column + 1; + + for ($i = $row - 1; $i >= 0; $i--) { + //判断上一行的 column 列是否有值 + if ($this->result[$i] == $column) { + return false; + } + + //左上角是否有值 + if ($leftup >= 0 && $this->result[$i] == $leftup) { + return false; + } + + //右下角是否有值 + if ($rightdown < 8 && $this->result[$i] == $rightdown) { + return false; + } + + $leftup--; + $rightdown++; + + } + + return true; + } + + //打印 + function printQueens() + { + for ($row = 0; $row < 8; $row++) { + for ($column = 0; $column < 8; $column++) { + if ($this->result[$row] == $column) { + echo 'Q'; + } else { + echo '*'; + } + } + echo '
'; + } + } +} + +$queen = new Queen(); +$queen->cal8queens(0); diff --git a/php/README.md b/php/README.md index 3aa96741..aabcd3ce 100644 --- a/php/README.md +++ b/php/README.md @@ -1,16 +1,23 @@ ## 数据结构与算法之美PHP实现 ### 项目运行 -* 依赖composer自动加载,php目录下执行`composer dump-autoload` +* 依赖composer自动加载,php目录下执行`composer dump-autoload` || `sh buildAutoLoad.sh` * 项目代码均在mac&php7环境下跑通 ### 项目实现 -#### 06 +#### 06_linkedlist * 单链表php实现 * 回文判断 -#### 07 + +#### 07_linkedlist * reverse 单链表反转 * checkCircle 链表中环的检测 * mergerSortedList 两个有序的链表合并 * deleteLastKth 删除链表倒数第n个结点 -* findMiddleNode 求链表的中间结点 \ No newline at end of file +* findMiddleNode 求链表的中间结点 + +#### 08_stack +* 链栈实现 + +#### 09_stack +* 队列链表实现 diff --git a/php/Stack/Compute.php b/php/Stack/Compute.php new file mode 100644 index 00000000..3e5624bd --- /dev/null +++ b/php/Stack/Compute.php @@ -0,0 +1,78 @@ += 48 && ord($arr[$i] <= 57)){ + array_push($numStack, $arr[$i]); + continue; + } + switch ($arr[$i]){ + case '+': + case '-': + $arrLen = count($operStack); + while ($operStack[$arrLen-1] === '*' || $operStack[$arrLen-1] === '/' || $operStack[$arrLen-1] === '-'){ + compute($numStack, $operStack); + $arrLen--; + } + array_push($operStack, $arr[$i]); + break; + case '*': + case '/': + case '(': + array_push($operStack, $arr[$i]); + break; + case ')': + $arrLen = count($operStack); + while ($operStack[$arrLen-1] !== '('){ + compute($numStack, $operStack); + $arrLen--; + } + array_pop($operStack); + break; + default: + throw new \Exception("不支持的运算符", 1); + break; + } + } + + $arrLen = count($operStack); + while ($operStack[$arrLen-1] !== NULL){ + compute($numStack, $operStack); + $arrLen--; + } + echo array_pop($numStack); +} + +//数字栈长度减一,运算符栈长度减一 +function compute(&$numStack, &$operStack){ + $num = array_pop($numStack); + switch (array_pop($operStack)) { + case '*': + array_push($numStack, array_pop($numStack) * $num); + break; + case '/': + array_push($numStack, array_pop($numStack) / $num); + break; + case '+': + array_push($numStack, array_pop($numStack) + $num); + break; + case '-': + array_push($numStack, array_pop($numStack) - $num); + break; + + } +} +expression('-1+2-(1+2*3)'); +echo PHP_EOL; +eval('echo -1+2-(1+2*3);'); \ No newline at end of file diff --git a/php/composer.json b/php/composer.json index ea60240e..ee6e4b1c 100644 --- a/php/composer.json +++ b/php/composer.json @@ -6,7 +6,10 @@ "autoload": { "psr-4": { "Algo_06\\": "06_linkedlist/", - "Algo_07\\": "07_linkedlist/" + "Algo_07\\": "07_linkedlist/", + "Algo_08\\": "08_stack/", + "Algo_09\\": "09_queue/", + "Algo_24\\": "24_tree/" } } } diff --git a/python/.vs/slnx.sqlite b/python/.vs/slnx.sqlite new file mode 100644 index 00000000..482c4f83 Binary files /dev/null and b/python/.vs/slnx.sqlite differ diff --git a/python/05_array/myarray.py b/python/05_array/myarray.py index af554804..e7fa40e8 100644 --- a/python/05_array/myarray.py +++ b/python/05_array/myarray.py @@ -1,60 +1,70 @@ -from typing import Optional # # 1) Insertion, deletion and random access of array # 2) Assumes int for element type # # Author: Wenru # + + class MyArray: """A simple wrapper around List. You cannot have -1 in the array. """ + def __init__(self, capacity: int): self._data = [] - self._count = 0 self._capacity = capacity - - def __getitem__(self, position: int) -> int: - """Support for subscript. - Perhaps better than the find() method below. - """ + + def __getitem__(self, position: int) -> object: return self._data[position] - def find(self, index: int) -> Optional[int]: - if index >= self._count or index <= -self._count: return None - return self._data[index] + def __setitem__(self, index: int, value: object): + self._data[index] = value + + def __len__(self) -> int: + return len(self._data) + + def __iter__(self): + for item in self._data: + yield item + + def find(self, index: int) -> object: + try: + return self._data[index] + except IndexError: + return None def delete(self, index: int) -> bool: - if index >= self._count or index <= -self._count: return False - self._data[index:-1] = self._data[index+1:] - self._count -= 1 - return True + try: + self._data.pop(index) + return True + except IndexError: + return False def insert(self, index: int, value: int) -> bool: - if index >= self._count or index <= -self._count: return False - if self._capacity == self._count: return False - self._data.insert(index, value) - self._count += 1 - return True - - def insert_to_tail(self, value: int) -> bool: - if self._count == self._capacity: return False - self._data.append(value) - self._count += 1 - return True - - def __repr__(self) -> str: - return " ".join(str(num) for num in self._data[:self._count]) + if len(self) >= self._capacity: + return False + else: + return self._data.insert(index, value) def print_all(self): - for num in self._data[:self._count]: - print(f"{num}", end=" ") - print("\n", flush=True) + for item in self: + print(item) + + +def test_myarray(): + array = MyArray(5) + array.insert(0, 3) + array.insert(0, 4) + array.insert(1, 5) + array.insert(3, 9) + array.insert(3, 10) + assert array.insert(0, 100) is False + assert len(array) == 5 + assert array.find(1) == 5 + assert array.delete(4) is True + array.print_all() + if __name__ == "__main__": - a = MyArray(6) - for i in range(6): - a.insert_to_tail(i) - - a.delete(2) - print(a) \ No newline at end of file + test_myarray() diff --git a/python/06_linkedlist/LRUCache.py b/python/06_linkedlist/LRUCache.py new file mode 100644 index 00000000..ed1d0086 --- /dev/null +++ b/python/06_linkedlist/LRUCache.py @@ -0,0 +1,107 @@ +# Definition for singly-linked list. +class DbListNode(object): + def __init__(self, x, y): + self.key = x + self.val = y + self.next = None + self.prev = None + + +class LRUCache: + ''' + leet code: 146 + 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。 + 它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 + 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。 + 当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间 + + 哈希表+双向链表 + 哈希表: 查询 O(1) + 双向链表: 有序, 增删操作 O(1) + + Author: Ben + ''' + + def __init__(self, capacity: int): + self.cap = capacity + self.hkeys = {} + # self.top和self.tail作为哨兵节点, 避免越界 + self.top = DbListNode(None, -1) + self.tail = DbListNode(None, -1) + self.top.next = self.tail + self.tail.prev = self.top + + def get(self, key: int) -> int: + + if key in self.hkeys.keys(): + # 更新结点顺序 + cur = self.hkeys[key] + # 跳出原位置 + cur.next.prev = cur.prev + cur.prev.next = cur.next + # 最近用过的置于链表首部 + top_node = self.top.next + self.top.next = cur + cur.prev = self.top + cur.next = top_node + top_node.prev = cur + + return self.hkeys[key].val + return -1 + + def put(self, key: int, value: int) -> None: + if key in self.hkeys.keys(): + cur = self.hkeys[key] + cur.val = value + # 跳出原位置 + cur.prev.next = cur.next + cur.next.prev = cur.prev + + # 最近用过的置于链表首部 + top_node = self.top.next + self.top.next = cur + cur.prev = self.top + cur.next = top_node + top_node.prev = cur + else: + # 增加新结点至首部 + cur = DbListNode(key, value) + self.hkeys[key] = cur + # 最近用过的置于链表首部 + top_node = self.top.next + self.top.next = cur + cur.prev = self.top + cur.next = top_node + top_node.prev = cur + if len(self.hkeys.keys()) > self.cap: + self.hkeys.pop(self.tail.prev.key) + # 去掉原尾结点 + self.tail.prev.prev.next = self.tail + self.tail.prev = self.tail.prev.prev + + def __repr__(self): + vals = [] + p = self.top.next + while p.next: + vals.append(str(p.val)) + p = p.next + return '->'.join(vals) + + +if __name__ == '__main__': + cache = LRUCache(2) + cache.put(1, 1) + cache.put(2, 2) + print(cache) + cache.get(1) # 返回 1 + cache.put(3, 3) # 该操作会使得密钥 2 作废 + print(cache) + cache.get(2) # 返回 -1 (未找到) + cache.put(4, 4) # 该操作会使得密钥 1 作废 + print(cache) + cache.get(1) # 返回 -1 (未找到) + cache.get(3) # 返回 3 + print(cache) + cache.get(4) # 返回 4 + print(cache) diff --git a/python/06_linkedlist/palindrome.py b/python/06_linkedlist/palindrome.py new file mode 100644 index 00000000..25dd69ef --- /dev/null +++ b/python/06_linkedlist/palindrome.py @@ -0,0 +1,54 @@ +""" + check a single-linked list whether a palindrome +""" + +import sys +# 引用当前文件夹下的single_linked_list +sys.path.append('singly_linked_list') +from singly_linked_list import SinglyLinkedList + +def reverse(head): + reverse_head = None + while head: + next = head._next + head._next = reverse_head + reverse_head = head + head = next + + return reverse_head + +def is_palindrome(l): + l.print_all() + slow = l._head + fast = l._head + position = 0 + while fast and fast._next: + slow = slow._next + fast = fast._next._next + position += 1 + + reverse_node = reverse(slow) + head_node = l._head + is_palin = True + while (head_node and reverse_node): + if (head_node.data == reverse_node.data): + head_node = head_node._next + reverse_node = reverse_node._next + else: + is_palin = False + break + + return is_palin + +if __name__ == '__main__': + # the result should be False, True, True, True, True + test_str_arr = ['ab', 'aa', 'aba', 'abba', 'abcba'] + for str in test_str_arr: + l = SinglyLinkedList() + for i in str: + l.insert_value_to_head(i) + + print(is_palindrome(l)) + + + diff --git a/python/06_linkedlist/singlyLinkedList.py b/python/06_linkedlist/singlyLinkedList.py new file mode 100644 index 00000000..ec090a91 --- /dev/null +++ b/python/06_linkedlist/singlyLinkedList.py @@ -0,0 +1,284 @@ +# 1.单链表的插入、删除、查找操作; +# 2.链表中存储的数据类型是Int +# +# Author:Lee + + +class Node(object): + """链表结构的Node节点""" + + def __init__(self, data, next_node=None): + """Node节点的初始化方法. + 参数: + data:存储的数据 + next:下一个Node节点的引用地址 + """ + self.__data = data + self.__next = next_node + + @property + def data(self): + """Node节点存储数据的获取. + 返回: + 当前Node节点存储的数据 + """ + return self.__data + + @data.setter + def data(self, data): + """Node节点存储数据的设置方法. + 参数: + data:新的存储数据 + """ + self.__data = data + + @property + def next_node(self): + """获取Node节点的next指针值. + 返回: + next指针数据 + """ + return self.__next + + @next_node.setter + def next_node(self, next_node): + """Node节点next指针的修改方法. + 参数: + next:新的下一个Node节点的引用 + """ + self.__next = next_node + + +class SinglyLinkedList(object): + """单向链表""" + + def __init__(self): + """单向列表的初始化方法.""" + self.__head = None + + def find_by_value(self, value): + """按照数据值在单向列表中查找. + 参数: + value:查找的数据 + 返回: + Node + """ + node = self.__head + while (node is not None) and (node.data != value): + node = node.next_node + return node + + def find_by_index(self, index): + """按照索引值在列表中查找. + 参数: + index:索引值 + 返回: + Node + """ + node = self.__head + pos = 0 + while (node is not None) and (pos != index): + node = node.next_node + pos += 1 + return node + + def insert_to_head(self, value): + """在链表的头部插入一个存储value数值的Node节点. + 参数: + value:将要存储的数据 + """ + node = Node(value) + node.next_node = self.__head + self.__head = node + + def insert_after(self, node, value): + """在链表的某个指定Node节点之后插入一个存储value数据的Node节点. + 参数: + node:指定的一个Node节点 + value:将要存储在新Node节点中的数据 + """ + if node is None: # 如果指定在一个空节点之后插入数据节点,则什么都不做 + return + + new_node = Node(value) + new_node.next_node = node.next + node.next = new_node + + def insert_before(self, node, value): + """在链表的某个指定Node节点之前插入一个存储value数据的Node节点. + 参数: + node:指定的一个Node节点 + value:将要存储在新的Node节点中的数据 + """ + if (node is None) or (self.__head is None): # 如果指定在一个空节点之前或者空链表之前插入数据节点,则什么都不做 + return + + if node == self.__head: # 如果是在链表头之前插入数据节点,则直接插入 + self.insert_to_head(value) + return + + new_node = Node(value) + pro = self.__head + not_found = False # 如果在整个链表中都没有找到指定插入的Node节点,则该标记量设置为True + while pro.next_node != node: # 寻找指定Node之前的一个Node + if pro.next_node is None: # 如果已经到了链表的最后一个节点,则表明该链表中没有找到指定插入的Node节点 + not_found = True + break + else: + pro = pro.next_node + if not not_found: + pro.next_node = new_node + new_node.next_node = node + + def delete_by_node(self, node): + """在链表中删除指定Node的节点. + 参数: + node:指定的Node节点 + """ + if self.__head is None: # 如果链表是空的,则什么都不做 + return + + if node == self.__head: # 如果指定删除的Node节点是链表的头节点 + self.__head = node.next_node + return + + pro = self.__head + not_found = False # 如果在整个链表中都没有找到指定删除的Node节点,则该标记量设置为True + while pro.next_node != node: + if pro.next_node is None: # 如果已经到链表的最后一个节点,则表明该链表中没有找到指定删除的Node节点 + not_found = True + break + else: + pro = pro.next_node + if not not_found: + pro.next_node = node.next_node + + def delete_by_value(self, value): + """在链表中删除指定存储数据的Node节点. + 参数: + value:指定的存储数据 + """ + if self.__head is None: # 如果链表是空的,则什么都不做 + return + + if self.__head.data == value: # 如果链表的头Node节点就是指定删除的Node节点 + self.__head = self.__head.next_node + + pro = self.__head + node = self.__head.next_node + not_found = False + while node.data != value: + if node.next_node is None: # 如果已经到链表的最后一个节点,则表明该链表中没有找到执行Value值的Node节点 + not_found = True + break + else: + pro = node + node = node.next_node + if not_found is False: + pro.next_node = node.next_node + + def delete_last_n_node(self, n): + """删除链表中倒数第N个节点. + 主体思路: + 设置快、慢两个指针,快指针先行,慢指针不动;当快指针跨了N步以后,快、慢指针同时往链表尾部移动, + 当快指针到达链表尾部的时候,慢指针所指向的就是链表的倒数第N个节点 + 参数: + n:需要删除的倒数第N个序数 + """ + fast = self.__head + slow = self.__head + step = 0 + + while step <= n: + fast = fast.next_node + step += 1 + + while fast.next_node is not None: + tmp = slow + fast = fast.next_node + slow = slow.next_node + + tmp.next_node = slow.next_node + + def find_mid_node(self): + """查找链表中的中间节点. + 主体思想: + 设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,则当快指针到达链表尾部的时候,慢指针指向链表的中间节点 + 返回: + node:链表的中间节点 + """ + fast = self.__head + slow = self.__head + + while fast.next_node is not None: + fast = fast.next_node.next_node + slow = slow.next_node + + return slow + + def create_node(self, value): + """创建一个存储value值的Node节点. + 参数: + value:将要存储在Node节点中的数据 + 返回: + 一个新的Node节点 + """ + return Node(value) + + def print_all(self): + """打印当前链表所有节点数据.""" + pos = self.__head + if pos is None: + print("当前链表还没有数据") + return + while pos.next_node is not None: + print(str(pos.data) + " --> ", end="") + pos = pos.next_node + print(str(pos.data)) + + def reversed_self(self): + """翻转链表自身.""" + if self.__head is None or self.__head.next is None: # 如果链表为空,或者链表只有一个节点 + return + + pre = self.__head + node = self.__head.next + while node is not None: + pre, node = self.__reversed_with_two_node(pre, node) + + self.__head.next = None + self.__head = pre + + def __reversed_with_two_node(self, pre, node): + """翻转相邻两个节点. + 参数: + pre:前一个节点 + node:当前节点 + 返回: + (pre,node):下一个相邻节点的元组 + """ + tmp = node.next_node + node.next_node = pre + pre = node # 这样写有点啰嗦,但是能让人更能看明白 + node = tmp + return pre, node + + def has_ring(self): + """检查链表中是否有环. + 主体思想: + 设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,如果快指针没有与慢指针相遇而是顺利到达链表尾部 + 说明没有环;否则,存在环 + 返回: + True:有环 + False:没有环 + """ + fast = self.__head + slow = self.__head + + while (fast.next_node is not None) and (fast is not None): + fast = fast.next_node + slow = slow.next_node + if fast == slow: + return True + + return False diff --git a/python/06_linkedlist/singly_linked_list.py b/python/06_linkedlist/singly_linked_list.py index f5565f39..db7daa13 100644 --- a/python/06_linkedlist/singly_linked_list.py +++ b/python/06_linkedlist/singly_linked_list.py @@ -6,23 +6,23 @@ """ from typing import Optional + class Node: - - def __init__(self, data: int, next=None): + + def __init__(self, data: int, next_node=None): self.data = data - self._next = next - + self._next = next_node + class SinglyLinkedList: def __init__(self): self._head = None - + def find_by_value(self, value: int) -> Optional[Node]: p = self._head while p and p.data != value: p = p._next - return p def find_by_index(self, index: int) -> Optional[Node]: @@ -31,22 +31,21 @@ def find_by_index(self, index: int) -> Optional[Node]: while p and position != index: p = p._next position += 1 - return p def insert_value_to_head(self, value: int): new_node = Node(value) self.insert_node_to_head(new_node) - + def insert_node_to_head(self, new_node: Node): if new_node: new_node._next = self._head self._head = new_node - + def insert_value_after(self, node: Node, value: int): new_node = Node(value) self.insert_node_after(node, new_node) - + def insert_node_after(self, node: Node, new_node: Node): if not node or not new_node: return @@ -66,7 +65,7 @@ def insert_node_before(self, node: Node, new_node: Node): current = self._head while current._next and current._next != node: current = current._next - if not current._next: # node is not even in the list + if not current._next: # node is not even in the list return new_node._next = node current._next = new_node @@ -83,13 +82,13 @@ def delete_by_node(self, node: Node): while current and current._next != node: current = current._next if not current: # node not in the list - return - current._next = None + return + current._next = node._next def delete_by_value(self, value: int): if not self._head or not value: return - fake_head = Node(value+1) + fake_head = Node(value + 1) fake_head._next = self._head prev, current = fake_head, self._head while current: @@ -100,17 +99,21 @@ def delete_by_value(self, value: int): if prev._next: prev._next = None self._head = fake_head._next # in case head.data == value - + def __repr__(self) -> str: nums = [] current = self._head while current: nums.append(current.data) current = current._next - if len(nums) > 0: - return "->".join(str(num) for num in nums) - else: - return "" + return "->".join(str(num) for num in nums) + + # 重写__iter__方法,方便for关键字调用打印值 + def __iter__(self): + node = self._head + while node: + yield node.data + node = node._next def print_all(self): current = self._head @@ -136,4 +139,6 @@ def print_all(self): l.delete_by_node(node11) l.delete_by_node(l._head) l.delete_by_value(13) - print(l) \ No newline at end of file + print(l) + for value in l: + print(value) diff --git a/python/07_linkedlist/linked_list_algo.py b/python/07_linkedlist/linked_list_algo.py index 94271265..4d69ae83 100644 --- a/python/07_linkedlist/linked_list_algo.py +++ b/python/07_linkedlist/linked_list_algo.py @@ -55,7 +55,7 @@ def merge_sorted_list(l1: Node, l2: Node) -> Optional[Node]: current = current._next current._next = p1 if p1 else p2 return fake_head._next - return p1 or p2 + return l1 or l2 # Remove nth node from the end # 删除倒数第n个节点。假设n大于0 diff --git a/python/08_stack/simple_browser.py b/python/08_stack/simple_browser.py new file mode 100644 index 00000000..ff7edfeb --- /dev/null +++ b/python/08_stack/simple_browser.py @@ -0,0 +1,75 @@ +""" + a simple browser realize + Author: zhenchao.zhu + 解答:我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈, + 并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。 + 当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据, + 那就说明没有页面可以点击前进按钮浏览了。 +""" + +import sys +# 引用当前文件夹下的single_linked_list +sys.path.append('linked_stack.py') +from linked_stack import LinkedStack +#from .linked_stack import LinkedStack + +class NewLinkedStack(LinkedStack): + + def is_empty(self): + return not self._top + + +class Browser(): + + def __init__(self): + self.forward_stack = NewLinkedStack() + self.back_stack = NewLinkedStack() + + def can_forward(self): + if self.back_stack.is_empty(): + return False + + return True + + def can_back(self): + if self.forward_stack.is_empty(): + return False + + return True + + def open(self, url): + print("Open new url %s" % url, end="\n") + self.forward_stack.push(url) + + def back(self): + if self.forward_stack.is_empty(): + return + + top = self.forward_stack.pop() + self.back_stack.push(top) + print("back to %s" % top, end="\n") + + def forward(self): + if self.back_stack.is_empty(): + return + + top = self.back_stack.pop() + self.forward_stack.push(top) + print("forward to %s" % top, end="\n") + + +if __name__ == '__main__': + + browser = Browser() + browser.open('a') + browser.open('b') + browser.open('c') + if browser.can_back(): + browser.back() + + if browser.can_forward(): + browser.forward() + + browser.back() + browser.back() + browser.back() diff --git a/python/09_queue/array_queue.py b/python/09_queue/array_queue.py index 40f34c28..1c34b018 100644 --- a/python/09_queue/array_queue.py +++ b/python/09_queue/array_queue.py @@ -16,9 +16,16 @@ def __init__(self, capacity: int): self._tail = 0 def enqueue(self, item: str) -> bool: - if self._tail == self._capacity: return False + if self._tail == self._capacity: + if self._head == 0: + return False + else: + for i in range(0, self._tail - self._head): + self._items[i] = self._items[i + self._head] + self._tail = self._tail - self._head + self._head = 0 - self._items.append(item) + self._items.insert(self._tail, item) self._tail += 1 return True @@ -27,6 +34,8 @@ def dequeue(self) -> Optional[str]: item = self._items[self._head] self._head += 1 return item + else: + return None def __repr__(self) -> str: return " ".join(item for item in self._items[self._head : self._tail]) diff --git a/python/09_queue/dynamic_array_queue.py b/python/09_queue/dynamic_array_queue.py new file mode 100644 index 00000000..b4228403 --- /dev/null +++ b/python/09_queue/dynamic_array_queue.py @@ -0,0 +1,51 @@ +""" + Author: Wenru +""" + +from typing import Optional + +class DynamicArrayQueue: + + def __init__(self, capacity: int): + self._items = [] + self._capacity = capacity + self._head = 0 + self._tail = 0 + + def enqueue(self, item: str) -> bool: + if self._tail == self._capacity: + if self._head == 0: return False + + self._items[0 : self._tail - self._head] = self._items[self._head : self._tail] + self._tail -= self._head + self._head = 0 + + if self._tail == len(self._items): + self._items.append(item) + else: + self._items[self._tail] = item + self._tail += 1 + return True + + def dequeue(self) -> Optional[str]: + if self._head != self._tail: + item = self._items[self._head] + self._head += 1 + return item + + def __repr__(self) -> str: + return " ".join(item for item in self._items[self._head:self._tail]) + +if __name__ == "__main__": + q = DynamicArrayQueue(10) + for i in range(10): + q.enqueue(str(i)) + print(q) + + for _ in range(3): + q.dequeue() + print(q) + + q.enqueue("7") + q.enqueue("8") + print(q) \ No newline at end of file diff --git a/python/09_queue/linked_queue.py b/python/09_queue/linked_queue.py new file mode 100644 index 00000000..4ee4ecf0 --- /dev/null +++ b/python/09_queue/linked_queue.py @@ -0,0 +1,58 @@ +""" + Queue based upon linked list + + Author: Wenru +""" + +from typing import Optional + +class Node: + + def __init__(self, data: str, next=None): + self.data = data + self._next = next + +class LinkedQueue: + + def __init__(self): + self._head: Optional[Node] = None + self._tail: Optional[Node] = None + + def enqueue(self, value: str): + new_node = Node(value) + if self._tail: + self._tail._next = new_node + else: + self._head = new_node + self._tail = new_node + + def dequeue(self) -> Optional[str]: + if self._head: + value = self._head.data + self._head = self._head._next + if not self._head: + self._tail = None + return value + + def __repr__(self) -> str: + values = [] + current = self._head + while current: + values.append(current.data) + current = current._next + return "->".join(value for value in values) + + +if __name__ == "__main__": + q = LinkedQueue() + for i in range(10): + q.enqueue(str(i)) + print(q) + + for _ in range(3): + q.dequeue() + print(q) + + q.enqueue("7") + q.enqueue("8") + print(q) diff --git a/python/11_sorts/sorts.py b/python/11_sorts/sorts.py new file mode 100644 index 00000000..aed133e5 --- /dev/null +++ b/python/11_sorts/sorts.py @@ -0,0 +1,105 @@ +""" + Bubble sort, insertion sort and selection sort + 冒泡排序、插入排序、选择排序 + + Author: Wenru +""" + +from typing import List + + +# 冒泡排序 +def bubble_sort(a: List[int]): + length = len(a) + if length <= 1: + return + + for i in range(length): + made_swap = False + for j in range(length - i - 1): + if a[j] > a[j + 1]: + a[j], a[j + 1] = a[j + 1], a[j] + made_swap = True + if not made_swap: + break + + +# 插入排序 +def insertion_sort(a: List[int]): + length = len(a) + if length <= 1: + return + + for i in range(1, length): + value = a[i] + j = i - 1 + while j >= 0 and a[j] > value: + a[j + 1] = a[j] + j -= 1 + a[j + 1] = value + + +# 选择排序 +def selection_sort(a: List[int]): + length = len(a) + if length <= 1: + return + + for i in range(length): + min_index = i + min_val = a[i] + for j in range(i, length): + if a[j] < min_val: + min_val = a[j] + min_index = j + a[i], a[min_index] = a[min_index], a[i] + + +def test_bubble_sort(): + test_array = [1, 1, 1, 1] + bubble_sort(test_array) + assert test_array == [1, 1, 1, 1] + test_array = [4, 1, 2, 3] + bubble_sort(test_array) + assert test_array == [1, 2, 3, 4] + test_array = [4, 3, 2, 1] + bubble_sort(test_array) + assert test_array == [1, 2, 3, 4] + + +def test_insertion_sort(): + test_array = [1, 1, 1, 1] + insertion_sort(test_array) + assert test_array == [1, 1, 1, 1] + test_array = [4, 1, 2, 3] + insertion_sort(test_array) + assert test_array == [1, 2, 3, 4] + test_array = [4, 3, 2, 1] + insertion_sort(test_array) + assert test_array == [1, 2, 3, 4] + + +def test_selection_sort(): + test_array = [1, 1, 1, 1] + selection_sort(test_array) + assert test_array == [1, 1, 1, 1] + test_array = [4, 1, 2, 3] + selection_sort(test_array) + assert test_array == [1, 2, 3, 4] + test_array = [4, 3, 2, 1] + selection_sort(test_array) + assert test_array == [1, 2, 3, 4] + + +if __name__ == "__main__": + array = [5, 6, -1, 4, 2, 8, 10, 7, 6] + bubble_sort(array) + print(array) + + array = [5, 6, -1, 4, 2, 8, 10, 7, 6] + insertion_sort(array) + print(array) + + array = [5, 6, -1, 4, 2, 8, 10, 7, 6] + selection_sort(array) + print(array) diff --git a/python/12_sorts/merge_sort.py b/python/12_sorts/merge_sort.py new file mode 100644 index 00000000..f1c5e71a --- /dev/null +++ b/python/12_sorts/merge_sort.py @@ -0,0 +1,65 @@ +""" + Author: Wenru +""" + +from typing import List + + +def merge_sort(a: List[int]): + _merge_sort_between(a, 0, len(a) - 1) + + +def _merge_sort_between(a: List[int], low: int, high: int): + # The indices are inclusive for both low and high. + if low < high: + mid = low + (high - low) // 2 + _merge_sort_between(a, low, mid) + _merge_sort_between(a, mid + 1, high) + _merge(a, low, mid, high) + + +def _merge(a: List[int], low: int, mid: int, high: int): + # a[low:mid], a[mid+1, high] are sorted. + i, j = low, mid + 1 + tmp = [] + while i <= mid and j <= high: + if a[i] <= a[j]: + tmp.append(a[i]) + i += 1 + else: + tmp.append(a[j]) + j += 1 + start = i if i <= mid else j + end = mid if i <= mid else high + tmp.extend(a[start:end + 1]) + a[low:high + 1] = tmp + + +def test_merge_sort(): + a1 = [3, 5, 6, 7, 8] + merge_sort(a1) + assert a1 == [3, 5, 6, 7, 8] + a2 = [2, 2, 2, 2] + merge_sort(a2) + assert a2 == [2, 2, 2, 2] + a3 = [4, 3, 2, 1] + merge_sort(a3) + assert a3 == [1, 2, 3, 4] + a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] + merge_sort(a4) + assert a4 == [-2, -1, 3, 3, 5, 7, 8, 9, 9] + + +if __name__ == "__main__": + a1 = [3, 5, 6, 7, 8] + a2 = [2, 2, 2, 2] + a3 = [4, 3, 2, 1] + a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] + merge_sort(a1) + print(a1) + merge_sort(a2) + print(a2) + merge_sort(a3) + print(a3) + merge_sort(a4) + print(a4) diff --git a/python/12_sorts/quick_sort.py b/python/12_sorts/quick_sort.py new file mode 100644 index 00000000..b4f923cf --- /dev/null +++ b/python/12_sorts/quick_sort.py @@ -0,0 +1,61 @@ +""" + Author: Wenru +""" + +from typing import List +import random + + +def quick_sort(a: List[int]): + _quick_sort_between(a, 0, len(a) - 1) + + +def _quick_sort_between(a: List[int], low: int, high: int): + if low < high: + # get a random position as the pivot + k = random.randint(low, high) + a[low], a[k] = a[k], a[low] + + m = _partition(a, low, high) # a[m] is in final position + _quick_sort_between(a, low, m - 1) + _quick_sort_between(a, m + 1, high) + + +def _partition(a: List[int], low: int, high: int): + pivot, j = a[low], low + for i in range(low + 1, high + 1): + if a[i] <= pivot: + j += 1 + a[j], a[i] = a[i], a[j] # swap + a[low], a[j] = a[j], a[low] + return j + + +def test_quick_sort(): + a1 = [3, 5, 6, 7, 8] + quick_sort(a1) + assert a1 == [3, 5, 6, 7, 8] + a2 = [2, 2, 2, 2] + quick_sort(a2) + assert a2 == [2, 2, 2, 2] + a3 = [4, 3, 2, 1] + quick_sort(a3) + assert a3 == [1, 2, 3, 4] + a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] + quick_sort(a4) + assert a4 == [-2, -1, 3, 3, 5, 7, 8, 9, 9] + + +if __name__ == "__main__": + a1 = [3, 5, 6, 7, 8] + a2 = [2, 2, 2, 2] + a3 = [4, 3, 2, 1] + a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] + quick_sort(a1) + print(a1) + quick_sort(a2) + print(a2) + quick_sort(a3) + print(a3) + quick_sort(a4) + print(a4) diff --git a/python/12_sorts/quicksort_twoway.py b/python/12_sorts/quicksort_twoway.py new file mode 100644 index 00000000..2a46ddd4 --- /dev/null +++ b/python/12_sorts/quicksort_twoway.py @@ -0,0 +1,65 @@ +import random + + +def QuickSort(arr): + # 双向排序: 提高非随机输入的性能 + # 不需要额外的空间,在待排序数组本身内部进行排序 + # 基准值通过random随机选取 + # 入参: 待排序数组, 数组开始索引 0, 数组结束索引 len(array)-1 + if arr is None or len(arr) < 1: + return arr + + def swap(arr, low, upper): + tmp = arr[low] + arr[low] = arr[upper] + arr[upper] = tmp + return arr + + def QuickSort_TwoWay(arr, low, upper): + # 小数组排序i可以用插入或选择排序 + # if upper-low < 50 : return arr + # 基线条件: low index = upper index; 也就是只有一个值的区间 + if low >= upper: + return arr + # 随机选取基准值, 并将基准值替换到数组第一个元素 + swap(arr, low, int(random.uniform(low, upper))) + temp = arr[low] + # 缓存边界值, 从上下边界同时排序 + i, j = low, upper + while True: + # 第一个元素是基准值,所以要跳过 + i += 1 + # 在小区间中, 进行排序 + # 从下边界开始寻找大于基准值的索引 + while i <= upper and arr[i] <= temp: + i += 1 + # 从上边界开始寻找小于基准值的索引 + # 因为j肯定大于i, 所以索引值肯定在小区间中 + while arr[j] > temp: + j -= 1 + # 如果小索引大于等于大索引, 说明排序完成, 退出排序 + if i >= j: + break + swap(arr, i, j) + # 将基准值的索引从下边界调换到索引分割点 + swap(arr, low, j) + QuickSort_TwoWay(arr, low, j - 1) + QuickSort_TwoWay(arr, j + 1, upper) + return arr + + return QuickSort_TwoWay(arr, 0, len(arr) - 1) + + +if __name__ == "__main__": + a1 = [3, 5, 6, 7, 8] + a2 = [2, 2, 2, 2] + a3 = [4, 3, 2, 1] + a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9] + QuickSort(a1) + print(a1) + QuickSort(a2) + print(a2) + QuickSort(a3) + print(a3) + QuickSort(a4) + print(a4) diff --git a/python/14_sorts/counting_sort.py b/python/14_sorts/counting_sort.py new file mode 100644 index 00000000..41a32ab8 --- /dev/null +++ b/python/14_sorts/counting_sort.py @@ -0,0 +1,40 @@ +""" + 计数排序 + + Author: Wenru +""" + +from typing import List +import itertools + +def counting_sort(a: List[int]): + if len(a) <= 1: return + + # a中有counts[i]个数不大于i + counts = [0] * (max(a) + 1) + for num in a: + counts[num] += 1 + counts = list(itertools.accumulate(counts)) + + # 临时数组,储存排序之后的结果 + a_sorted = [0] * len(a) + for num in reversed(a): + index = counts[num] - 1 + a_sorted[index] = num + counts[num] -= 1 + + a[:] = a_sorted + + +if __name__ == "__main__": + a1 = [1, 2, 3, 4] + counting_sort(a1) + print(a1) + + a2 = [1, 1, 1, 1] + counting_sort(a2) + print(a2) + + a3 = [4, 5, 0, 9, 3, 3, 1, 9, 8, 7] + counting_sort(a3) + print(a3) diff --git a/python/15_bsearch/bsearch.py b/python/15_bsearch/bsearch.py new file mode 100644 index 00000000..dc091b29 --- /dev/null +++ b/python/15_bsearch/bsearch.py @@ -0,0 +1,22 @@ +""" + Author: Wenru +""" + +from typing import List + +def bsearch(nums: List[int], target: int) -> int: + """Binary search of a target in a sorted array + without duplicates. If such a target does not exist, + return -1, othewise, return its index. + """ + low, high = 0, len(nums) - 1 + while low <= high: + mid = low + (high - low) // 2 + if nums[mid] == target: + return mid + elif nums[mid] < target: + low = mid + 1 + else: + high = mid - 1 + + return -1 diff --git a/python/15_bsearch/bsearch_recursion.py b/python/15_bsearch/bsearch_recursion.py new file mode 100644 index 00000000..d16e58da --- /dev/null +++ b/python/15_bsearch/bsearch_recursion.py @@ -0,0 +1,22 @@ +""" + Author: dreamkong +""" + +from typing import List + + +def bsearch(nums: List[int], target: int) -> int: + return bsearch_internally(nums, 0, len(nums)-1, target) + + +def bsearch_internally(nums: List[int], low: int, high: int, target: int) -> int: + if low > high: + return -1 + + mid = low+int((high-low) >> 2) + if nums[mid] == target: + return mid + elif nums[mid] < target: + return bsearch_internally(nums, mid+1, high, target) + else: + return bsearch_internally(nums, low, mid-1, target) diff --git a/python/16_bsearch/bsearch_variants.py b/python/16_bsearch/bsearch_variants.py new file mode 100644 index 00000000..0b699556 --- /dev/null +++ b/python/16_bsearch/bsearch_variants.py @@ -0,0 +1,95 @@ +""" + Author: Wenru + Fix: nzjia +""" + +from typing import List + +def bsearch_left(nums: List[int], target: int) -> int: + """Binary search of the index of the first element + equal to a given target in the ascending sorted array. + If not found, return -1. + """ + low, high = 0, len(nums) - 1 + while low <= high: + mid = low + (high - low) // 2 + if nums[mid] < target: + low = mid + 1 + else: + high = mid - 1 + if low < len(nums) and nums[low] == target: + return low + else: + return -1 + + +def bsearch_right(nums: List[int], target: int) -> int: + """Binary search of the index of the last element + equal to a given target in the ascending sorted array. + If not found, return -1. + """ + low, high = 0, len(nums) - 1 + while low <= high: + mid = low + (high - low) // 2 + if nums[mid] <= target: + low = mid + 1 + else: + high = mid - 1 + if high >= 0 and nums[high] == target: + return high + else: + return -1 + + +def bsearch_left_not_less(nums: List[int], target: int) -> int: + """Binary search of the index of the first element + not less than a given target in the ascending sorted array. + If not found, return -1. + """ + low, high = 0, len(nums) - 1 + while low <= high: + mid = low + (high - low) // 2 + if nums[mid] < target: + low = mid + 1 + else: + high = mid - 1 + if low < len(nums) and nums[low] >= target: + return low + else: + return -1 + +def bsearch_right_not_greater(nums: List[int], target: int) -> int: + """Binary search of the index of the last element + not greater than a given target in the ascending sorted array. + If not found, return -1. + """ + low, high = 0, len(nums) - 1 + while low <= high: + mid = low + (high - low) // 2 + if nums[mid] <= target: + low = mid + 1 + else: + high = mid - 1 + if high >= 0 and nums[high] <= target: + return high + else: + return -1 + +if __name__ == "__main__": + a = [1, 1, 2, 3, 4, 6, 7, 7, 7, 7, 10, 22] + + print(bsearch_left(a, 0) == -1) + print(bsearch_left(a, 7) == 6) + print(bsearch_left(a, 30) == -1) + + print(bsearch_right(a, 0) == -1) + print(bsearch_right(a, 7) == 9) + print(bsearch_right(a, 30) == -1) + + print(bsearch_left_not_less(a, 0) == 0) + print(bsearch_left_not_less(a, 5) == 5) + print(bsearch_left_not_less(a, 30) == -1) + + print(bsearch_right_not_greater(a, 0) == -1) + print(bsearch_right_not_greater(a, 6) == 5) + print(bsearch_right_not_greater(a, 30) == 11) diff --git a/python/17_skiplist/skip_list.py b/python/17_skiplist/skip_list.py new file mode 100644 index 00000000..52069eb1 --- /dev/null +++ b/python/17_skiplist/skip_list.py @@ -0,0 +1,91 @@ +""" + An implementation of skip list. + The list stores positive integers without duplicates. + + 跳表的一种实现方法。 + 跳表中储存的是正整数,并且储存的是不重复的。 + + Author: Wenru +""" + +from typing import Optional +import random + +class ListNode: + + def __init__(self, data: Optional[int] = None): + self._data = data + self._forwards = [] # Forward pointers + +class SkipList: + + _MAX_LEVEL = 16 + + def __init__(self): + self._level_count = 1 + self._head = ListNode() + self._head._forwards = [None] * type(self)._MAX_LEVEL + + def find(self, value: int) -> Optional[ListNode]: + p = self._head + for i in range(self._level_count - 1, -1, -1): # Move down a level + while p._forwards[i] and p._forwards[i]._data < value: + p = p._forwards[i] # Move along level + + return p._forwards[0] if p._forwards[0] and p._forwards[0]._data == value else None + + def insert(self, value: int): + level = self._random_level() + if self._level_count < level: self._level_count = level + new_node = ListNode(value) + new_node._forwards = [None] * level + update = [self._head] * level # update is like a list of prevs + + p = self._head + for i in range(level - 1, -1, -1): + while p._forwards[i] and p._forwards[i]._data < value: + p = p._forwards[i] + + update[i] = p # Found a prev + + for i in range(level): + new_node._forwards[i] = update[i]._forwards[i] # new_node.next = prev.next + update[i]._forwards[i] = new_node # prev.next = new_node + + def delete(self, value): + update = [None] * self._level_count + p = self._head + for i in range(self._level_count - 1, -1, -1): + while p._forwards[i] and p._forwards[i]._data < value: + p = p._forwards[i] + update[i] = p + + if p._forwards[0] and p._forwards[0]._data == value: + for i in range(self._level_count - 1, -1, -1): + if update[i]._forwards[i] and update[i]._forwards[i]._data == value: + update[i]._forwards[i] = update[i]._forwards[i]._forwards[i] # Similar to prev.next = prev.next.next + + def _random_level(self, p: float = 0.5) -> int: + level = 1 + while random.random() < p and level < type(self)._MAX_LEVEL: + level += 1 + return level + + def __repr__(self) -> str: + values = [] + p = self._head + while p._forwards[0]: + values.append(str(p._forwards[0]._data)) + p = p._forwards[0] + return "->".join(values) + + +if __name__ == "__main__": + l = SkipList() + for i in range(10): + l.insert(i) + print(l) + p = l.find(7) + print(p._data) + l.delete(3) + print(l) \ No newline at end of file diff --git a/python/17_skiplist/skip_list_comments.py b/python/17_skiplist/skip_list_comments.py new file mode 100644 index 00000000..de7de483 --- /dev/null +++ b/python/17_skiplist/skip_list_comments.py @@ -0,0 +1,118 @@ +import random + + +class SkipListNode(object): + def __init__(self, val, high=1): + # 节点存储的值 + self.data = val + # 节点对应索引层的深度 + self.deeps = [None] * high + + +class SkipList(object): + """ + An implementation of skip list. + The list stores positive integers without duplicates. + 跳表的一种实现方法。 + 跳表中储存的是正整数,并且储存的是不重复的。 + Author: Ben + """ + + def __init__(self): + # 索引层的最大深度 + self.__MAX_LEVEL = 16 + # 跳表的高度 + self._high = 1 + # 每一索引层的首节点, 默认值为None + self._head = SkipListNode(None, self.__MAX_LEVEL) + + def find(self, val): + cur = self._head + # 从索引的顶层, 逐层定位要查找的值 + # 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点 + for i in range(self._high - 1, -1, -1): + # 同一索引层内, 查找小于插入值的最大值对应的节点 + while cur.deeps[i] and cur.deeps[i].data < val: + cur = cur.deeps[i] + + if cur.deeps[0] and cur.deeps[0].data == val: + return cur.deeps[0] + return None + + def insert(self, val): + ''' + 新增时, 通过随机函数获取要更新的索引层数, + 要对低于给定高度的索引层添加新结点的指针 + ''' + high = self.randomLevel() + if self._high < high: + self._high = high + # 申请新结点 + newNode = SkipListNode(val, high) + # cache用来缓存对应索引层中小于插入值的最大节点 + cache = [self._head] * high + cur = self._head + + # 在低于随机高度的每一个索引层寻找小于插入值的节点 + for i in range(high - 1, -1, -1): + # 每个索引层内寻找小于带插入值的节点 + # ! 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点 + while cur.deeps[i] and cur.deeps[i].data < val: + cur = cur.deeps[i] + cache[i] = cur + + # 在小于高度的每个索引层中插入新结点 + for i in range(high): + # new.next = prev.next \ prev.next = new.next + newNode.deeps[i] = cache[i].deeps[i] + cache[i].deeps[i] = newNode + + def delete(self, val): + ''' + 删除时, 要将每个索引层中对应的节点都删掉 + ''' + # cache用来缓存对应索引层中小于插入值的最大节点 + cache = [None] * self._high + cur = self._head + # 缓存每一个索引层定位小于插入值的节点 + for i in range(self._high - 1, -1, -1): + while cur.deeps[i] and cur.deeps[i].data < val: + cur = cur.deeps[i] + cache[i] = cur + # 如果给定的值存在, 更新索引层中对应的节点 + if cur.deeps[0] and cur.deeps[0].data == val: + for i in range(self._high): + if cache[i].deeps[i] and cache[i].deeps[i].data == val: + cache[i].deeps[i] = cache[i].deeps[i].deeps[i] + + def randomLevel(self, p=0.25): + ''' + #define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */ + https://github.com/antirez/redis/blob/unstable/src/t_zset.c + ''' + high = 1 + for _ in range(self.__MAX_LEVEL - 1): + if random.random() < p: + high += 1 + return high + + def __repr__(self): + vals = [] + p = self._head + while p.deeps[0]: + vals.append(str(p.deeps[0].data)) + p = p.deeps[0] + return '->'.join(vals) + + +if __name__ == '__main__': + sl = SkipList() + for i in range(100): + sl.insert(i) + print(sl) + p = sl.find(7) + print(p.data) + sl.delete(37) + print(sl) + sl.delete(37.5) + print(sl) diff --git a/python/23_binarytree/binary_search_tree.py b/python/23_binarytree/binary_search_tree.py new file mode 100644 index 00000000..5500c5c7 --- /dev/null +++ b/python/23_binarytree/binary_search_tree.py @@ -0,0 +1,292 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from queue import Queue +import math + + +class TreeNode: + def __init__(self, val=None): + self.val = val + self.left = None + self.right = None + self.parent = None + + +class BinarySearchTree: + def __init__(self, val_list=[]): + self.root = None + for n in val_list: + self.insert(n) + + def insert(self, data): + """ + 插入 + :param data: + :return: + """ + assert(isinstance(data, int)) + + if self.root is None: + self.root = TreeNode(data) + else: + n = self.root + while n: + p = n + if data < n.val: + n = n.left + else: + n = n.right + + new_node = TreeNode(data) + new_node.parent = p + + if data < p.val: + p.left = new_node + else: + p.right = new_node + + return True + + def search(self, data): + """ + 搜索 + 返回bst中所有值为data的节点列表 + :param data: + :return: + """ + assert(isinstance(data, int)) + + # 所有搜索到的节点 + ret = [] + + n = self.root + while n: + if data < n.val: + n = n.left + else: + if data == n.val: + ret.append(n) + n = n.right + + return ret + + def delete(self, data): + """ + 删除 + :param data: + :return: + """ + assert (isinstance(data, int)) + + # 通过搜索得到需要删除的节点 + del_list = self.search(data) + + for n in del_list: + # 父节点为空,又不是根节点,已经不在树上,不用再删除 + if n.parent is None and n != self.root: + continue + else: + self._del(n) + + def _del(self, node): + """ + 删除 + 所删除的节点N存在以下情况: + 1. 没有子节点:直接删除N的父节点指针 + 2. 有一个子节点:将N父节点指针指向N的子节点 + 3. 有两个子节点:找到右子树的最小节点M,将值赋给N,然后删除M + :param data: + :return: + """ + # 1 + if node.left is None and node.right is None: + # 情况1和2,根节点和普通节点的处理方式不同 + if node == self.root: + self.root = None + else: + if node.val < node.parent.val: + node.parent.left = None + else: + node.parent.right = None + + node.parent = None + # 2 + elif node.left is None and node.right is not None: + if node == self.root: + self.root = node.right + self.root.parent = None + node.right = None + else: + if node.val < node.parent.val: + node.parent.left = node.right + else: + node.parent.right = node.right + + node.right.parent = node.parent + node.parent = None + node.right = None + elif node.left is not None and node.right is None: + if node == self.root: + self.root = node.left + self.root.parent = None + node.left = None + else: + if node.val < node.parent.val: + node.parent.left = node.left + else: + node.parent.right = node.left + + node.left.parent = node.parent + node.parent = None + node.left = None + # 3 + else: + min_node = node.right + # 找到右子树的最小值节点 + if min_node.left: + min_node = min_node.left + + if node.val != min_node.val: + node.val = min_node.val + self._del(min_node) + # 右子树的最小值节点与被删除节点的值相等,再次删除原节点 + else: + self._del(min_node) + self._del(node) + + def get_min(self): + """ + 返回最小值节点 + :return: + """ + if self.root is None: + return None + + n = self.root + while n.left: + n = n.left + return n.val + + def get_max(self): + """ + 返回最大值节点 + :return: + """ + if self.root is None: + return None + + n = self.root + while n.right: + n = n.right + return n.val + + def in_order(self): + """ + 中序遍历 + :return: + """ + if self.root is None: + return [] + + return self._in_order(self.root) + + def _in_order(self, node): + if node is None: + return [] + + ret = [] + n = node + ret.extend(self._in_order(n.left)) + ret.append(n.val) + ret.extend(self._in_order(n.right)) + + return ret + + def __repr__(self): + # return str(self.in_order()) + print(str(self.in_order())) + return self._draw_tree() + + def _bfs(self): + """ + bfs + 通过父子关系记录节点编号 + :return: + """ + if self.root is None: + return [] + + ret = [] + q = Queue() + # 队列[节点,编号] + q.put((self.root, 1)) + + while not q.empty(): + n = q.get() + + if n[0] is not None: + ret.append((n[0].val, n[1])) + q.put((n[0].left, n[1]*2)) + q.put((n[0].right, n[1]*2+1)) + + return ret + + def _draw_tree(self): + """ + 可视化 + :return: + """ + nodes = self._bfs() + + if not nodes: + print('This tree has no nodes.') + return + + layer_num = int(math.log(nodes[-1][1], 2)) + 1 + + prt_nums = [] + + for i in range(layer_num): + prt_nums.append([None]*2**i) + + for v, p in nodes: + row = int(math.log(p ,2)) + col = p % 2**row + prt_nums[row][col] = v + + prt_str = '' + for l in prt_nums: + prt_str += str(l)[1:-1] + '\n' + + return prt_str + + +if __name__ == '__main__': + nums = [4, 2, 5, 6, 1, 7, 3] + bst = BinarySearchTree(nums) + print(bst) + + # 插入 + bst.insert(1) + bst.insert(4) + print(bst) + + # 搜索 + for n in bst.search(2): + print(n.parent.val, n.val) + + # 删除 + bst.insert(6) + bst.insert(7) + print(bst) + bst.delete(7) + print(bst) + bst.delete(6) + print(bst) + bst.delete(4) + print(bst) + + # min max + print(bst.get_max()) + print(bst.get_min()) diff --git a/python/23_binarytree/binary_tree.py b/python/23_binarytree/binary_tree.py new file mode 100644 index 00000000..dae39734 --- /dev/null +++ b/python/23_binarytree/binary_tree.py @@ -0,0 +1,70 @@ +""" + Pre-order, in-order and post-order traversal of binary trees. + + Author: Wenru Dong +""" +from typing import TypeVar, Generic, Generator, Optional + +T = TypeVar("T") + +class TreeNode(Generic[T]): + def __init__(self, value: T): + self.val = value + self.left = None + self.right = None + + +# Pre-order traversal +def pre_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]: + if root: + yield root.val + yield from pre_order(root.left) + yield from pre_order(root.right) + +# In-order traversal +def in_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]: + if root: + yield from in_order(root.left) + yield root.val + yield from in_order(root.right) + +# Post-order traversal +def post_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]: + if root: + yield from post_order(root.left) + yield from post_order(root.right) + yield root.val + + +if __name__ == "__main__": + + singer = TreeNode("Taylor Swift") + + genre_country = TreeNode("Country") + genre_pop = TreeNode("Pop") + + album_fearless = TreeNode("Fearless") + album_red = TreeNode("Red") + album_1989 = TreeNode("1989") + album_reputation = TreeNode("Reputation") + + song_ls = TreeNode("Love Story") + song_wh = TreeNode("White Horse") + song_wanegbt = TreeNode("We Are Never Ever Getting Back Together") + song_ikywt = TreeNode("I Knew You Were Trouble") + song_sio = TreeNode("Shake It Off") + song_bb = TreeNode("Bad Blood") + song_lwymmd = TreeNode("Look What You Made Me Do") + song_g = TreeNode("Gorgeous") + + singer.left, singer.right = genre_country, genre_pop + genre_country.left, genre_country.right = album_fearless, album_red + genre_pop.left, genre_pop.right = album_1989, album_reputation + album_fearless.left, album_fearless.right = song_ls, song_wh + album_red.left, album_red.right = song_wanegbt, song_ikywt + album_1989.left, album_1989.right = song_sio, song_bb + album_reputation.left, album_reputation.right = song_lwymmd, song_g + + print(list(pre_order(singer))) + print(list(in_order(singer))) + print(list(post_order(singer))) diff --git a/python/24_tree/binary_search_tree.py b/python/24_tree/binary_search_tree.py new file mode 100644 index 00000000..f5a3f870 --- /dev/null +++ b/python/24_tree/binary_search_tree.py @@ -0,0 +1,64 @@ +""" + Binary search tree + + Author: Wenru Dong +""" +from typing import Optional + +class TreeNode: + def __init__(self, value: int): + self.val = value + self.left = None + self.right = None + +class BinarySearchTree: + def __init__(self): + self._root = None + + def find(self, value: int) -> Optional[TreeNode]: + node = self._root + while node and node.val != value: + node = node.left if node.val > value else node.right + return node + + def insert(self, value: int): + if not self._root: + self._root = TreeNode(value) + return + parent = None + node = self._root + while node: + parent = node + node = node.left if node.val > value else node.right + new_node = TreeNode(value) + if parent.val > value: + parent.left = new_node + else: + parent.right = new_node + + def delete(self, value: int): + node = self._root + parent = None + while node and node.val != value: + parent = node + node = node.left if node.val > value else node.right + if not node: return + + # 要删除的节点有两个子节点 + if node.left and node.right: + successor = node.right + successor_parent = node + while successor.left: + successor_parent = successor + successor = successor.left + node.val = successor.val + parent, node = successor_parent, successor + + # 删除节点是叶子节点或者仅有一个子节点 + child = node.left if node.left else node.right + if not parent: + self._root = child + elif parent.left == node: + parent.left = child + else: + parent.right = child diff --git a/python/26_red_black_tree/red_black_tree.py b/python/26_red_black_tree/red_black_tree.py new file mode 100644 index 00000000..ed67017f --- /dev/null +++ b/python/26_red_black_tree/red_black_tree.py @@ -0,0 +1,449 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from queue import Queue +import pygraphviz as pgv +import random + +OUTPUT_PATH = 'E:/' + + +class TreeNode: + def __init__(self, val=None, color=None): + self.val = val + assert color in ['r', 'b'] + self.color = 'red' if color == 'r' else 'black' + + self.left = None + self.right = None + self.parent = None + + def is_black(self): + return self.color == 'black' + + def set_black(self): + self.color = 'black' + return + + def set_red(self): + self.color = 'red' + + +class RedBlackTree: + """ + 红黑树实现 + 参考资料: + 1. 《算法导论》 + 第13章 红黑树 + 13.3 插入 p178 + 13.4 删除 p183 + + 2. 红黑树(二):删除 + https://zhuanlan.zhihu.com/p/25402654 + """ + def __init__(self, val_list=None): + self.root = None + self.black_leaf = TreeNode(color='b') # 共用的黑色叶子节点 + + # 可用数组初始化 + if type(val_list) is list: + for n in val_list: + assert type(n) is int + self.insert(n) + + def search(self, val): + """ + 搜索 + :param val: + :return: + """ + if self.root is None: + return None + + n = self.root + while n != self.black_leaf: + if val < n.val: + n = n.left + elif val > n.val: + n = n.right + else: + return n + return None + + def insert(self, val): + """ + 插入 + :param val: + :return: + """ + assert type(val) is int + + new_node = TreeNode(val, 'r') # 新插入的节点为红色 + + # 根节点 + if self.root is None: + self.root = new_node + else: + n = self.root + while n != self.black_leaf: # 黑色叶子节点 + p = n + if val < n.val: + n = n.left + elif val > n.val: + n = n.right + else: + raise KeyError('val:{} already exists') # 该值已存在,插入失败 + + if val < p.val: + p.left = new_node + else: + p.right = new_node + new_node.parent = p + + new_node.left = new_node.right = self.black_leaf + # 插入后调整 + self._insert_fixup(new_node) + + def _insert_fixup(self, node): + """ + 插入调整 + 参考资料:《算法导论》 13.3 p178-179 + :param node: + :return: + """ + n = node + while n is not self.root and not n.parent.is_black(): + # 父p 叔u 祖父g + p = self.parent(n) + u = self.bro(p) + g = self.parent(p) + + if not u.is_black(): # case 1 + p.set_black() # case 1 + u.set_black() # case 1 + g.set_red() # case 1 + n = g # case 1 + continue + + if p == g.left: # p为左结点 + if n == p.right: # case 2 + self.rotate_l(p) # case 2 + n, p = p, n # case 2 + p.set_black() # case 3 + g.set_red() # case 3 + self.rotate_r(g) # case 3 + else: # p为右节点 + if n == p.left: # case 2 + self.rotate_r(p) # case 2 + n, p = p, n # case 2 + p.set_black() # case 3 + g.set_red() # case 3 + self.rotate_l(g) # case 3 + + # 根节点强制置黑,有两种情况根节点是红色: + # 1. 新插入时是红色 + # 2. 经过case 1调整过后变红色 + self.root.color = 'black' + + def delete(self, val): + """ + 删除 + :param val: + :return: + """ + assert type(val) is int + + n = self.search(val) + if n is None: + print('can not find any nodes with value: {}'.format(val)) + return + + self._delete_node(n) + + def _delete_node(self, node): + """ + 删除节点内部实现 + 参考资料:《算法导论》 13.4 p183-184 + 实现方式有微调,当n有2个子节点时,将s拷贝至n,转为删除s(s最多有一个子节点) + :param node: + :return: + """ + n = node + + # n的子节点个数等于2 + if self.children_count(n) == 2: + # 寻找n的后继s + s = n.right + while s.left != self.black_leaf: + s = s.left + n.val = s.val + # 将删除n转化为删除s + n = s + + # n的子节点个数小于2 + if n.left == self.black_leaf: + c = n.right + else: + c = n.left + self._transplant(n, c) + + # 删除的节点是黑色,需要调整 + if n.is_black(): + self._delete_fixup(c) + return + + def _delete_fixup(self, node): + """ + 删除调整 + 参考资料:《算法导论》 13.4 p185-187 + :param node: + :return: + """ + n = node + while n != self.root and n.is_black(): + p = self.parent(n) + b = self.bro(n) + + # 左右节点对称 + if p.left == n: + if not b.is_black(): + b.set_black() # case 1 + p.set_red() # case 1 + self.rotate_l(p) # case 1 + # new bro after rotate + b = self.bro(n) # case 1 + + if b.left.is_black() and b.right.is_black(): + b.set_red() # case 2 + n = p # case 2 + else: + if b.right.is_black(): + b.left.set_black() # case 3 + b.set_red() # case 3 + self.rotate_r(b) # case 3 + # new bro after rotate + b = self.bro(n) # case 3 + + # 注意,因为p可能是红或黑,所以不能直接赋值颜色,只能copy + b.color = p.color # case 4 + p.set_black() # case 4 + b.right.set_black() # case 4 + self.rotate_l(p) # case 4 + # trick, 调整结束跳出while + n = self.root # case 4 + else: + if not b.is_black(): + b.set_black() # case 1 + p.set_red() # case 1 + self.rotate_r(p) # case 1 + # new bro after rotate + b = self.bro(n) # case 1 + + if b.left.is_black() and b.right.is_black(): + b.set_red() # case 2 + n = p # case 2 + else: + if b.left.is_black(): + b.right.set_black() # case 3 + b.set_red() # case 3 + self.rotate_l(b) # case 3 + # new bro after rotate + b = self.bro(n) # case 3 + + # 注意,因为p可能是红或黑,所以不能直接赋值颜色,只能copy + b.color = p.color # case 4 + p.set_black() # case 4 + b.left.set_black() # case 4 + self.rotate_r(p) # case 4 + # trick, 调整结束跳出while + n = self.root # case 4 + + # 将n设为黑色,从上面while循环跳出,情况有两种 + # 1. n是根节点,直接无视附加的黑色 + # 2. n是红色的节点,则染黑 + n.set_black() + + def _transplant(self, n1, n2): + """ + 节点移植, n2 -> n1 + :param n1: 原节点 + :param n2: 移植节点 + :return: + """ + if n1 == self.root: + if n2 != self.black_leaf: + self.root = n2 + n2.parent = None + else: + self.root = None # 只有删除根节点时会进来 + else: + p = self.parent(n1) + if p.left == n1: + p.left = n2 + else: + p.right = n2 + + n2.parent = p + + def rotate_l(self, node): + """ + 左旋 + :param node: + :return: + """ + if node is None: + return + + if node.right is self.black_leaf: + return + # raise Exception('try rotate left , but the node "{}" has no right child'.format(node.val)) + + p = self.parent(node) + x = node + y = node.right + + # node为根节点时,p为None,旋转后要更新根节点指向 + if p is not None: + if x == p.left: + p.left = y + else: + p.right = y + else: + self.root = y + + x.parent, y.parent = y, p + + if y.left != self.black_leaf: + y.left.parent = x + + x.right, y.left = y.left, x + + def rotate_r(self, node): + """ + 右旋 + :param node: + :return: + """ + if node is None: + return + + if node.left is self.black_leaf: + return + # raise Exception('try rotate right , but the node "{}" has no left child'.format(node.val)) + + p = self.parent(node) + x = node + y = node.left + + # 同左旋 + if p is not None: + if x == p.left: + p.left = y + else: + p.right = y + else: + self.root = y + + x.parent, y.parent = y, p + + if y.right is not None: + y.right.parent = x + + x.left, y.right = y.right, x + + @staticmethod + def bro(node): + """ + 获取兄弟节点 + :param node: + :return: + """ + if node is None or node.parent is None: + return None + else: + p = node.parent + if node == p.left: + return p.right + else: + return p.left + + @staticmethod + def parent(node): + """ + 获取父节点 + :param node: + :return: + """ + if node is None: + return None + else: + return node.parent + + def children_count(self, node): + """ + 获取子节点个数 + :param node: + :return: + """ + return 2 - [node.left, node.right].count(self.black_leaf) + + def draw_img(self, img_name='Red_Black_Tree.png'): + """ + 画图 + 用pygraphviz画出节点和箭头 + 箭头的红色和黑色分别代表左和右 + :param img_name: + :return: + """ + if self.root is None: + return + + tree = pgv.AGraph(directed=True, strict=True) + + q = Queue() + q.put(self.root) + + while not q.empty(): + n = q.get() + if n != self.black_leaf: # 黑色叶子的连线由各个节点自己画 + tree.add_node(n.val, color=n.color) + # 画父节点箭头 + # if n.parent is not None: + # tree.add_edge(n.val, n.parent.val) + + for c in [n.left, n.right]: + q.put(c) + color = 'red' if c == n.left else 'black' + if c != self.black_leaf: + tree.add_edge(n.val, c.val, color=color) + else: + tree.add_edge(n.val, 'None', color=color) + + tree.graph_attr['epsilon'] = '0.01' + tree.layout('dot') + tree.draw(OUTPUT_PATH + img_name) + return True + + +if __name__ == '__main__': + rbt = RedBlackTree() + + # insert + nums = list(range(1, 25)) + # random.shuffle(nums) + for num in nums: + rbt.insert(num) + + # search + search_num = 23 + n = rbt.search(search_num) + if n is not None: + print(n) + else: + print('node {} not found'.format(search_num)) + + # delete + rbt.delete(4) + + # draw image + rbt.draw_img('rbt.png') diff --git a/python/28_binary_heap/binary_heap.py b/python/28_binary_heap/binary_heap.py new file mode 100644 index 00000000..5e5ddf09 --- /dev/null +++ b/python/28_binary_heap/binary_heap.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import math +import random + + +class BinaryHeap: + """ + 大顶堆 + """ + def __init__(self, data=None, capacity=100): + self._data = [] + self._capacity = capacity + if type(data) is list: + if len(data) > self._capacity: + raise Exception('Heap oversize, capacity:{}, data size:{}'.format(self._capacity, len(data))) + self._type_assert(data) + self._data = data + + self._length = len(self._data) + + def heapify(self): + """ + 堆化 + :return: + """ + self._heapify(self._data, self._length-1) + + def _heapify(self, data, tail_idx): + """ + 堆化内部实现 + :param data: 需要堆化的数据 + :param tail_idx: 尾元素的索引 + :return: + """ + # heapify data[:tail_idx+1] + if tail_idx <= 0: + return + + # idx of the Last Parent node + lp = (tail_idx - 1) // 2 + + for i in range(lp, -1, -1): + self._heap_down(data, i, tail_idx) + + @staticmethod + def _heap_down(data, idx, tail_idx): + """ + 将指定的位置堆化 + :param data: 需要堆化的数据 + :param idx: data: 中需要堆化的位置 + :param tail_idx: 尾元素的索引 + :return: + """ + assert type(data) is list + + lp = (tail_idx - 1) // 2 + # top-down + while idx <= lp: + # Left and Right Child index + lc = 2 * idx + 1 + rc = lc + 1 + + # right child exists + if rc <= tail_idx: + tmp = lc if data[lc] > data[rc] else rc + else: + tmp = lc + + if data[tmp] > data[idx]: + data[tmp], data[idx] = data[idx], data[tmp] + idx = tmp + else: + break + + def insert(self, num): + """ + 插入 + :param num: + :return: + """ + if self._length < self._capacity: + if self._insert(self._data, num): + self._length += 1 + return True + return False + + @staticmethod + def _insert(data, num): + """ + 堆中插入元素的内部实现 + :param data: + :param num: + :return: + """ + assert type(data) is list + assert type(num) is int + + data.append(num) + length = len(data) + + # idx of New Node + nn = length - 1 + # bottom-up + while nn > 0: + p = (nn-1) // 2 + if data[nn] > data[p]: + data[nn], data[p] = data[p], data[nn] + nn = p + else: + break + + return True + + def get_top(self): + """ + 取堆顶 + :return: + """ + if self._length <= 0: + return None + return self._data[0] + + def remove_top(self): + """ + 取堆顶 + :return: + """ + ret = None + if self._length > 0: + ret = self._remove_top(self._data) + self._length -= 1 + return ret + + @staticmethod + def _remove_top(data): + """ + 取堆顶内部实现 + :param data: + :return: + """ + assert type(data) is list + + length = len(data) + if length == 0: + return None + + data[0], data[-1] = data[-1], data[0] + ret = data.pop() + length -= 1 + + # length == 0 or == 1, return + if length > 1: + BinaryHeap._heap_down(data, 0, length-1) + + return ret + + @staticmethod + def _type_assert(nums): + assert type(nums) is list + for n in nums: + assert type(n) is int + + @staticmethod + def _draw_heap(data): + """ + 格式化打印 + :param data: + :return: + """ + length = len(data) + + if length == 0: + return 'empty heap' + + ret = '' + for i, n in enumerate(data): + ret += str(n) + # 每行最后一个换行 + if i == 2**int(math.log(i+1, 2)+1) - 2 or i == len(data) - 1: + ret += '\n' + else: + ret += ', ' + + return ret + + def __repr__(self): + return self._draw_heap(self._data) + + +if __name__ == '__main__': + nums = list(range(10)) + random.shuffle(nums) + + bh = BinaryHeap(nums) + print('--- before heapify ---') + print(bh) + + # heapify + bh.heapify() + print('--- after heapify ---') + print(bh) + + # insert + print('--- insert ---') + if bh.insert(8): + print('insert success') + else: + print('insert fail') + print(bh) + + # get top + print('--- get top ---') + print('get top of the heap: {}'.format(bh.get_top())) + bh.remove_top() + print(bh) diff --git a/python/28_binary_heap/binary_heap_sort.py b/python/28_binary_heap/binary_heap_sort.py new file mode 100644 index 00000000..f0506d4f --- /dev/null +++ b/python/28_binary_heap/binary_heap_sort.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from binary_heap import BinaryHeap + + +class BinaryHeapSort(BinaryHeap): + def __init__(self): + super(BinaryHeapSort, self).__init__() + + def sort(self, nums): + """ + 排序 + 1. 堆化,大顶堆 + 2. 排序,从后往前遍历,首尾元素互换,子数组堆化 + :param nums: + :return: + """ + assert type(nums) is list + length = len(nums) + + if length <= 1: + return + + self._type_assert(nums) + + # heapify + self._heapify(nums, length-1) + + # sort + for i in range(length-1, 0, -1): + nums[0], nums[i] = nums[i], nums[0] + self._heap_down(nums, 0, i-1) + + return + + +if __name__ == '__main__': + bhs = BinaryHeapSort() + nums = [3, 5, 2, 6, 1, 7, 6] + + print('--- before sort ---') + print(nums) + + bhs.sort(nums) + print('--- after sort ---') + print(nums) diff --git a/python/28_binary_heap/heap.py b/python/28_binary_heap/heap.py new file mode 100644 index 00000000..b934057f --- /dev/null +++ b/python/28_binary_heap/heap.py @@ -0,0 +1,178 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import math +import random + + +class Heap: + def __init__(self, nums=None, capacity=100): + self._data = [] + self._capacity = capacity + if type(nums) == list and len(nums) <= self._capacity: + for n in nums: + assert type(n) is int + self._data.append(n) + self._length = len(self._data) + self._heapify() + + def _heapify(self): + if self._length <= 1: + return + + # idx of the Last Parent node + lp = (self._length - 2) // 2 + + for i in range(lp, -1, -1): + self._heap_down(i) + + def _heap_down(self, idx): + pass + + def insert(self, num): + pass + + def get_top(self): + if self._length <= 0: + return None + return self._data[0] + + def remove_top(self): + if self._length <= 0: + return None + + self._data[0], self._data[-1] = self._data[-1], self._data[0] + ret = self._data.pop() + self._length -= 1 + self._heap_down(0) + + return ret + + def get_data(self): + return self._data + + def get_length(self): + return self._length + + @staticmethod + def _draw_heap(data): + """ + 格式化打印 + :param data: + :return: + """ + length = len(data) + + if length == 0: + return 'empty heap' + + ret = '' + for i, n in enumerate(data): + ret += str(n) + # 每行最后一个换行 + if i == 2 ** int(math.log(i + 1, 2) + 1) - 2 or i == len(data) - 1: + ret += '\n' + else: + ret += ', ' + + return ret + + def __repr__(self): + return self._draw_heap(self._data) + + +class MaxHeap(Heap): + def _heap_down(self, idx): + if self._length <= 1: + return + + lp = (self._length - 2) // 2 + + while idx <= lp: + lc = 2 * idx + 1 + rc = lc + 1 + + if rc <= self._length-1: + tmp = lc if self._data[lc] > self._data[rc] else rc + else: + tmp = lc + + if self._data[tmp] > self._data[idx]: + self._data[tmp], self._data[idx] = self._data[idx], self._data[tmp] + idx = tmp + else: + break + + def insert(self, num): + if self._length >= self._capacity: + return False + + self._data.append(num) + self._length += 1 + + nn = self._length - 1 + while nn > 0: + p = (nn-1) // 2 + + if self._data[nn] > self._data[p]: + self._data[nn], self._data[p] = self._data[p], self._data[nn] + nn = p + else: + break + + return True + + +class MinHeap(Heap): + def _heap_down(self, idx): + if self._length <= 1: + return + + lp = (self._length - 2) // 2 + + while idx <= lp: + lc = 2 * idx + 1 + rc = lc + 1 + + if rc <= self._length-1: + tmp = lc if self._data[lc] < self._data[rc] else rc + else: + tmp = lc + + if self._data[tmp] < self._data[idx]: + self._data[tmp], self._data[idx] = self._data[idx], self._data[tmp] + idx = tmp + else: + break + + def insert(self, num): + if self._length >= self._capacity: + return False + + self._data.append(num) + self._length += 1 + + nn = self._length - 1 + while nn > 0: + p = (nn-1) // 2 + + if self._data[nn] < self._data[p]: + self._data[nn], self._data[p] = self._data[p], self._data[nn] + nn = p + else: + break + + return True + + +if __name__ == '__main__': + nums = list(range(10)) + random.shuffle(nums) + + max_h = MaxHeap(nums) + print('--- max heap ---') + print(max_h) + + print('--- min heap ---') + min_h = MinHeap(nums) + print(min_h) diff --git a/python/28_binary_heap/priority_queue.py b/python/28_binary_heap/priority_queue.py new file mode 100644 index 00000000..32645348 --- /dev/null +++ b/python/28_binary_heap/priority_queue.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import math + + +class QueueNode: + def __init__(self, priority, data=None): + assert type(priority) is int and priority >= 0 + self.priority = priority + self.data = data + + def __repr__(self): + return str((self.priority, self.data)) + + +class PriorityQueue: + def __init__(self, capacity=100): + self._capacity = capacity + self._q = [] + self._length = 0 + + def enqueue(self, priority, data=None): + if self._length >= self._capacity: + return False + + new_node = QueueNode(priority, data) + self._q.append(new_node) + self._length += 1 + + # update queue + nn = self._length - 1 + while nn > 0: + p = (nn - 1) // 2 + if self._q[nn].priority < self._q[p].priority: + self._q[nn], self._q[p] = self._q[p], self._q[nn] + nn = p + else: + break + + return True + + def dequeue(self): + if self._length <= 0: + raise Exception('the queue is empty....') + + self._q[0], self._q[-1] = self._q[-1], self._q[0] + ret = self._q.pop() + self._length -= 1 + + if self._length > 1: + # update queue + lp = (self._length - 2) // 2 + idx = 0 + + while idx <= lp: + lc = 2 * idx + 1 + rc = lc + 1 + + if rc <= self._length - 1: + tmp = lc if self._q[lc].priority < self._q[rc].priority else rc + else: + tmp = lc + + if self._q[tmp].priority < self._q[idx].priority: + self._q[tmp], self._q[idx] = self._q[idx], self._q[tmp] + idx = tmp + else: + break + return ret + + def get_length(self): + return self._length + + @staticmethod + def _draw_heap(data): + """ + 格式化打印 + :param data: + :return: + """ + length = len(data) + + if length == 0: + return 'empty' + + ret = '' + for i, n in enumerate(data): + ret += str(n) + # 每行最后一个换行 + if i == 2 ** int(math.log(i + 1, 2) + 1) - 2 or i == len(data) - 1: + ret += '\n' + else: + ret += ', ' + + return ret + + def __repr__(self): + def formater(node): + assert type(node) is QueueNode + return node.priority, node.data + + data = list(map(formater, self._q)) + return self._draw_heap(data) + + +if __name__ == '__main__': + pq = PriorityQueue() + pq.enqueue(5, 'Watch TV') + pq.enqueue(2, 'Learning') + pq.enqueue(10, 'Go Sleep') + pq.enqueue(0, 'Go Home') + pq.enqueue(7, 'Mobile Games') + print(pq) + + while pq.get_length() > 0: + print(pq.dequeue()) diff --git a/python/28_binary_heap/top_k.py b/python/28_binary_heap/top_k.py new file mode 100644 index 00000000..8123a70b --- /dev/null +++ b/python/28_binary_heap/top_k.py @@ -0,0 +1,39 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import random +from heap import MinHeap + + +def top_k(nums, k): + """ + 返回数组的前k大元素 + :param nums: + :param k: + :return: + """ + if len(nums) <= k: + return nums + + min_h = MinHeap(nums[:k], k) + for i in range(k, len(nums)): + tmp = min_h.get_top() + if nums[i] > tmp: + min_h.remove_top() + min_h.insert(nums[i]) + + return min_h.get_data() + + +if __name__ == '__main__': + nums = [] + k = 3 + + for i in range(20): + nums.append(random.randint(1, 100)) + + print('--- nums ---') + print(nums) + + print('--- top {} ---'.format(k)) + print(top_k(nums, k)) diff --git a/python/28_heap/heap.py b/python/28_heap/heap.py new file mode 100644 index 00000000..f3961c64 --- /dev/null +++ b/python/28_heap/heap.py @@ -0,0 +1,96 @@ +""" + Max-heap + + Author: Wenru Dong +""" + +from typing import Optional, List + +class Heap: + def __init__(self, capacity: int): + self._data = [0] * (capacity + 1) + self._capacity = capacity + self._count = 0 + + @classmethod + def _parent(cls, child_index: int) -> int: + """The parent index.""" + return child_index // 2 + + @classmethod + def _left(cls, parent_index: int) -> int: + """The left child index.""" + return 2 * parent_index + + @classmethod + def _right(cls, parent_index: int) -> int: + """The right child index.""" + return 2 * parent_index + 1 + + def _siftup(self) -> None: + i, parent = self._count, Heap._parent(self._count) + while parent and self._data[i] > self._data[parent]: + self._data[i], self._data[parent] = self._data[parent], self._data[i] + i, parent = parent, Heap._parent(parent) + + @classmethod + def _siftdown(cls, a: List[int], count: int, root_index: int = 1) -> None: + i = larger_child_index = root_index + while True: + left, right = cls._left(i), cls._right(i) + if left <= count and a[i] < a[left]: + larger_child_index = left + if right <= count and a[larger_child_index] < a[right]: + larger_child_index = right + if larger_child_index == i: break + a[i], a[larger_child_index] = a[larger_child_index], a[i] + i = larger_child_index + + def insert(self, value: int) -> None: + if self._count >= self._capacity: return + self._count += 1 + self._data[self._count] = value + self._siftup() + + def remove_max(self) -> Optional[int]: + if self._count: + result = self._data[1] + self._data[1] = self._data[self._count] + self._count -= 1 + Heap._siftdown(self._data, self._count) + return result + + @classmethod + def build_heap(cls, a: List[int]) -> None: + """Data in a needs to start from index 1.""" + for i in range((len(a) - 1)//2, 0, -1): + cls._siftdown(a, len(a) - 1, i) + + @classmethod + def sort(cls, a: List[int]) -> None: + """Data in a needs to start from index 1.""" + cls.build_heap(a) + k = len(a) - 1 + while k > 1: + a[1], a[k] = a[k], a[1] + k -= 1 + cls._siftdown(a, k) + + def __repr__(self): + return self._data[1 : self._count + 1].__repr__() + + +if __name__ == "__main__": + hp = Heap(10) + hp.insert(3) + hp.insert(9) + hp.insert(1) + hp.insert(8) + hp.insert(7) + hp.insert(3) + print(hp) + for _ in range(6): + print(hp.remove_max()) + a = [0, 6, 3, 4, 0, 9, 2, 7, 5, -2, 8, 1, 6, 10] + Heap.sort(a) + print(a[1:]) \ No newline at end of file diff --git a/python/28_heap/min_heap.py b/python/28_heap/min_heap.py new file mode 100644 index 00000000..f086e281 --- /dev/null +++ b/python/28_heap/min_heap.py @@ -0,0 +1,108 @@ +class Heap(object): + ''' + 索引从0开始的小顶堆 + 参考: https://github.com/python/cpython/blob/master/Lib/heapq.py + + author: Ben + ''' + + def __init__(self, nums): + self._heap = nums + + def _siftup(self, pos): + ''' + 从上向下的堆化 + 将pos节点的子节点中的最值提升到pos位置 + ''' + start = pos + startval = self._heap[pos] + n = len(self._heap) + # 完全二叉树特性 + child = pos * 2 + 1 + # 比较叶子节点 + while child < n: + right = child + 1 + # 平衡二叉树的特性, 大的都在右边 + if right < n and not self._heap[right] > self._heap[child]: + child = right + self._heap[pos] = self._heap[child] + pos = child + child = pos * 2 + 1 + self._heap[pos] = startval + + # 此时只有pos是不确定的 + self._siftdown(start, pos) + + def _siftdown(self, start, pos): + ''' + 最小堆: 大于start的节点, 除pos外已经是最小堆 + 以pos为叶子节点, start为根节点之间的元素进行排序. 将pos叶子节点交换到正确的排序位置 + 操作: 从叶子节点开始, 当父节点的值大于子节点时, 父节点的值降低到子节点 + ''' + startval = self._heap[pos] + while pos > start: + parent = (pos - 1) >> 1 + parentval = self._heap[parent] + if parentval > startval: + self._heap[pos] = parentval + pos = parent + continue + break + self._heap[pos] = startval + + def heapify(self): + ''' + 堆化: 从后向前(从下向上)的方式堆化, _siftup中pos节点的子树已经是有序的, + 这样要排序的节点在慢慢减少 + 1. 因为n/2+1到n的节点是叶子节点(完全二叉树的特性), 它们没有子节点, + 所以, 只需要堆化n/2到0的节点, 以对应的父节点为根节点, 将最值向上筛选, + 然后交换对应的根节点和查找到的最值 + 2. 因为开始时待排序树的根节点还没有排序, 为了保证根节点的有序, + 需要将子树中根节点交换到正确顺序 + ''' + n = len(self._heap) + for i in reversed(range(n // 2)): + self._siftup(i) + + def heappop(self): + ''' + 弹出堆首的最值 O(logn) + ''' + tail = self._heap.pop() + # 为避免破环完全二叉树特性, 将堆尾元素填充到堆首 + # 此时, 只有堆首是未排序的, 只需要一次从上向下的堆化 + if self._heap: + peak = self._heap[0] + self._heap[0] = tail + self._siftup(0) + return peak + return tail + + def heappush(self, val): + ''' + 添加元素到堆尾 O(logn) + ''' + n = len(self._heap) + self._heap.append(val) + # 此时只有堆尾的节点是未排序的, 将添加的节点迭代到正确的位置 + self._siftdown(0, n) + + def __repr__(self): + vals = [str(i) for i in self._heap] + return '>'.join(vals) + + +if __name__ == '__main__': + h = Heap([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + h.heapify() + print(h) + print(h.heappop()) + print(h) + h.heappush(3.5) + print(h) + h.heappush(0.1) + print(h) + h.heappush(0.5) + print(h) + print(h.heappop()) + print(h) diff --git a/python/31_bfs_dfs/bfs_dfs.py b/python/31_bfs_dfs/bfs_dfs.py new file mode 100644 index 00000000..59cebd87 --- /dev/null +++ b/python/31_bfs_dfs/bfs_dfs.py @@ -0,0 +1,88 @@ +""" + Breadth-first search and depth-first search. + + Author: Wenru Dong +""" + +from typing import List, Optional, Generator, IO +from collections import deque + +class Graph: + """Undirected graph.""" + def __init__(self, num_vertices: int): + self._num_vertices = num_vertices + self._adjacency = [[] for _ in range(num_vertices)] + + def add_edge(self, s: int, t: int) -> None: + self._adjacency[s].append(t) + self._adjacency[t].append(s) + + def _generate_path(self, s: int, t: int, prev: List[Optional[int]]) -> Generator[str, None, None]: + if prev[t] or s != t: + yield from self._generate_path(s, prev[t], prev) + yield str(t) + + def bfs(self, s: int, t: int) -> IO[str]: + """Print out the path from Vertex s to Vertex t + using bfs. + """ + if s == t: return + + visited = [False] * self._num_vertices + visited[s] = True + q = deque() + q.append(s) + prev = [None] * self._num_vertices + + while q: + v = q.popleft() + for neighbour in self._adjacency[v]: + if not visited[neighbour]: + prev[neighbour] = v + if neighbour == t: + print("->".join(self._generate_path(s, t, prev))) + return + visited[neighbour] = True + q.append(neighbour) + + def dfs(self, s: int, t: int) -> IO[str]: + """Print out a path from Vertex s to Vertex t + using dfs. + """ + found = False + visited = [False] * self._num_vertices + prev = [None] * self._num_vertices + + def _dfs(from_vertex: int) -> None: + nonlocal found + if found: return + visited[from_vertex] = True + if from_vertex == t: + found = True + return + for neighbour in self._adjacency[from_vertex]: + if not visited[neighbour]: + prev[neighbour] = from_vertex + _dfs(neighbour) + + _dfs(s) + print("->".join(self._generate_path(s, t, prev))) + + +if __name__ == "__main__": + + graph = Graph(8) + + graph.add_edge(0, 1) + graph.add_edge(0, 3) + graph.add_edge(1, 2) + graph.add_edge(1, 4) + graph.add_edge(2, 5) + graph.add_edge(3, 4) + graph.add_edge(4, 5) + graph.add_edge(4, 6) + graph.add_edge(5, 7) + graph.add_edge(6, 7) + + graph.bfs(0, 7) + graph.dfs(0, 7) diff --git a/python/31_bfs_dfs/graph.py b/python/31_bfs_dfs/graph.py new file mode 100644 index 00000000..55b25419 --- /dev/null +++ b/python/31_bfs_dfs/graph.py @@ -0,0 +1,75 @@ +# -*- coding:utf-8 -*- + + +class Undigraph(object): + + + def __init__(self, vertex_num): + self.v_num = vertex_num + self.adj_tbl = [] + for i in range(self.v_num + 1): + self.adj_tbl.append([]) + + def add_edge(self, s, t): + if s > self.v_num or t > self.v_num: + return False + self.adj_tbl[s].append(t) + self.adj_tbl[t].append(s) + return True + + def __len__(self): + return self.v_num + + def __getitem__(self, ind): + if ind > self.v_num: + raise IndexError("No Such Vertex!") + return self.adj_tbl[ind] + + def __repr__(self): + return str(self.adj_tbl) + + def __str__(self): + return str(self.adj_tbl) + + +class Digraph(object): + + + def __init__(self, vertex_num): + self.v_num = vertex_num + self.adj_tbl = [] + for i in range(self.v_num + 1): + self.adj_tbl.append([]) + + def add_edge(self, frm, to): + if frm > self.v_num or to > self.v_num: + return False + self.adj_tbl[frm].append(to) + + def __len__(self): + return self.v_num + + def __getitem__(self, ind): + if ind > self.v_num: + raise IndexError("No such vertex!") + return self.ajd_tbl[ind] + + def __repr__(self): + return str(self.adj_tbl) + + def __str__(self): + return str(self.adj_tbl) + + +if __name__ == '__main__': + ug = Undigraph(10) + ug.add_edge(1, 9) + ug.add_edge(1, 3) + ug.add_edge(3, 2) + print(ug.adj_tbl) + + dg = Digraph(10) + dg.add_edge(1, 9) + dg.add_edge(1, 3) + dg.add_edge(3, 4) + print(dg.adj_tbl) diff --git a/python/31_bfs_dfs/graph_application.py b/python/31_bfs_dfs/graph_application.py new file mode 100644 index 00000000..8a4ec346 --- /dev/null +++ b/python/31_bfs_dfs/graph_application.py @@ -0,0 +1,45 @@ +# -*- coding:utf-8 -*- + + + +from collections import deque +from graph import Undigraph + +def find_vertex_by_degree(graph, s, degree): + if len(graph) <= 1: + return [] + if degree == 0: + return [s] + d_vertices = [] + queue = deque() + prev = [-1] * len(graph) + visited = [False] * len(graph) + visited[s] = True + queue.append(s) + while len(queue) > 0: + sz = len(queue) + for i in range(sz): + v = queue.popleft() + for adj_v in graph[v]: + if not visited[adj_v]: + prev[adj_v] = v + visited[adj_v] = True + queue.append(adj_v) + degree -= 1 + if degree == 0 and len(queue) != 0: + return queue + + +if __name__ == '__main__': + g = Undigraph(8) + g.add_edge(0, 1) + g.add_edge(0, 3) + g.add_edge(1, 2) + g.add_edge(1, 4) + g.add_edge(2, 5) + g.add_edge(3, 4) + g.add_edge(4, 5) + g.add_edge(4, 6) + g.add_edge(5, 7) + g.add_edge(6, 7) + print(find_vertex_by_degree(g, 0, 4)) diff --git a/python/32_bf_rk/bf_rk.py b/python/32_bf_rk/bf_rk.py new file mode 100644 index 00000000..0cd23990 --- /dev/null +++ b/python/32_bf_rk/bf_rk.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from time import time + + +def bf(main, pattern): + """ + 字符串匹配,bf暴搜 + :param main: 主串 + :param pattern: 模式串 + :return: + """ + n = len(main) + m = len(pattern) + + if n <= m: + return 0 if pattern == main else -1 + + for i in range(n-m+1): + for j in range(m): + if main[i+j] == pattern[j]: + if j == m-1: + return i + else: + continue + else: + break + return -1 + + +def simple_hash(s, start, end): + """ + 计算子串的哈希值 + 每个字符取acs-ii码后求和 + :param s: + :param start: + :param end: + :return: + """ + assert start <= end + + ret = 0 + for c in s[start: end+1]: + ret += ord(c) + return ret + + +def rk(main, pattern): + n = len(main) + m = len(pattern) + + if n <= m: + return 0 if pattern == main else -1 + + # 子串哈希值表 + hash_memo = [None] * (n-m+1) + hash_memo[0] = simple_hash(main, 0, m-1) + for i in range(1, n-m+1): + hash_memo[i] = hash_memo[i-1] - simple_hash(main, i-1, i-1) + simple_hash(main, i+m-1, i+m-1) + + # 模式串哈希值 + hash_p = simple_hash(pattern, 0, m-1) + + for i, h in enumerate(hash_memo): + # 可能存在哈希冲突 + if h == hash_p: + if pattern == main[i:i+m]: + return i + else: + continue + return -1 + + +if __name__ == '__main__': + m_str = 'a'*10000 + p_str = 'a'*200+'b' + + print('--- time consume ---') + t = time() + print('[bf] result:', bf(m_str, p_str)) + print('[bf] time cost: {0:.5}s'.format(time()-t)) + + t = time() + print('[rk] result:', rk(m_str, p_str)) + print('[rk] time cost: {0:.5}s'.format(time()-t)) + + print('') + print('--- search ---') + m_str = 'thequickbrownfoxjumpsoverthelazydog' + p_str = 'jump' + print('[bf] result:', bf(m_str, p_str)) + print('[rk] result:', rk(m_str, p_str)) diff --git a/python/33_bm/bm.py b/python/33_bm/bm.py new file mode 100644 index 00000000..74a9a34b --- /dev/null +++ b/python/33_bm/bm.py @@ -0,0 +1,77 @@ +""" + Boyer-Moore string-search algorithm. + + Author: Wenru Dong +""" + +from typing import List, Tuple + +SIZE = 256 + +def _generate_bad_character_table(pattern: str) -> List[int]: + bc = [-1] * SIZE + for i, char in enumerate(pattern): + bc[ord(char)] = i + return bc + + +def _generate_good_suffix_table(pattern: str) -> Tuple[List[bool], List[int]]: + m = len(pattern) + # prefix[k] records whether the last k-character suffix of pattern + # can match with the first k-character prefix of pattern. + # suffix[k] records the starting index of the last substring of + # pattern that can match with the last k-character suffix of pattern. + prefix, suffix = [False] * m, [-1] * m + # For each substring patter[:i+1], we find the common suffix with + # pattern, and the starting index of this common suffix. + # This way we can re-write previous suffix[k] to record the index + # as large as possible, hence the last substring. + for i in range(m - 1): + j = i # starting index of the common suffix + k = 0 # length of the common suffix + while j >= 0 and pattern[j] == pattern[~k]: + j -= 1 + k += 1 + suffix[k] = j + 1 + if j == -1: prefix[k] = True + return (prefix, suffix) + + +def _move_by_good_suffix(bad_character_index: int, suffix: List[int], prefix: List[bool]) -> int: + k = len(suffix) - 1 - bad_character_index + if suffix[k] != -1: return bad_character_index - suffix[k] + 1 + # Test from k - 1 + for r, can_match_prefix in enumerate(reversed(prefix[:k]), bad_character_index + 2): + if can_match_prefix: return r + return len(suffix) + + +def bm(s: str, pattern: str) -> int: + bc = _generate_bad_character_table(pattern) + prefix, suffix = _generate_good_suffix_table(pattern) + n, m = len(s), len(pattern) + i = 0 + while i <= n - m: + j = m - 1 # bad character index in pattern + while j >= 0: + if s[i + j] != pattern[j]: break + j -= 1 + if j < 0: return i + + x = j - bc[ord(s[i + j])] + y = 0 + if j < m - 1: + y = _move_by_good_suffix(j, suffix, prefix) + i += max(x, y) + return -1 + + +if __name__ == "__main__": + + s = "Here is a simple example" + pattern = "example" + print(bm(s, pattern)) + + s = "abcdcccdc" + pattern = "cccd" + print(s.find(pattern) == bm(s, pattern)) \ No newline at end of file diff --git a/python/33_bm/bm_.py b/python/33_bm/bm_.py new file mode 100644 index 00000000..7d615c4e --- /dev/null +++ b/python/33_bm/bm_.py @@ -0,0 +1,120 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +SIZE = 256 + + +def bm(main, pattern): + """ + BM算法 + 匹配规则: + 1. 坏字符规则 + 2. 好字符规则 + :param main: + :param pattern: + :return: + """ + assert type(main) is str and type(pattern) is str + n, m = len(main), len(pattern) + + if n <= m: + return 0 if main == pattern else -1 + + # bc + bc = [-1] * SIZE + generate_bc(pattern, m, bc) + + # gs + suffix = [-1] * m + prefix = [False] * m + generate_gs(pattern, m, suffix, prefix) + + i = 0 + while i < n-m+1: + j = m - 1 + while j >= 0: + if main[i+j] != pattern[j]: + break + else: + j -= 1 + + # pattern整个已被匹配,返回 + if j == -1: + return i + + # 1. bc规则计算后移位数 + x = j - bc[ord(main[i+j])] + + # 2. gs规则计算后移位数 + y = 0 + if j != m - 1: # 存在gs + y = move_by_gs(j, m, suffix, prefix) + + i += max(x, y) + + return -1 + + +def generate_bc(pattern, m, bc): + """ + 生成坏字符哈希表 + :param pattern: + :param m: + :param bc: + :return: + """ + for i in range(m): + bc[ord(pattern[i])] = i + + +def generate_gs(pattern, m, suffix, prefix): + """ + 好后缀预处理 + :param pattern: + :param m: + :param suffix: + :param prefix: + :return: + """ + for i in range(m-1): + k = 0 # pattern[:i+1]和pattern的公共后缀长度 + for j in range(i, -1, -1): + if pattern[j] == pattern[m-1-k]: + k += 1 + suffix[k] = j + if j == 0: + prefix[k] = True + else: + break + + +def move_by_gs(j, m, suffix, prefix): + """ + 通过好后缀计算移动值 + 需要处理三种情况: + 1. 整个好后缀在pattern仍能找到 + 2. 好后缀里存在 *后缀子串* 能和pattern的 *前缀* 匹配 + 3. 其他 + :param j: + :param m: + :param suffix: + :param prefix: + :return: + """ + k = m - 1 - j # j指向从后往前的第一个坏字符,k是此次匹配的好后缀的长度 + + if suffix[k] != -1: # 1. 整个好后缀在pattern剩余字符中仍有出现 + return j - suffix[k] + 1 + else: + for r in range(j+2, m): # 2. 后缀子串从长到短搜索 + if prefix[m-r]: + return r + return m # 3. 其他情况 + + +if __name__ == '__main__': + print('--- search ---') + m_str = 'dfasdeeeetewtweyyyhtruuueyytewtweyyhtrhrth' + p_str = 'eyytewtweyy' + print('[Built-in Functions] result:', m_str.find(p_str)) + print('[bm] result:', bm(m_str, p_str)) diff --git a/python/34_kmp/kmp.py b/python/34_kmp/kmp.py new file mode 100644 index 00000000..552f3c95 --- /dev/null +++ b/python/34_kmp/kmp.py @@ -0,0 +1,61 @@ +""" + KMP algorithm + + Author: Wenru Dong +""" + +from typing import List + +def kmp(s: int, pattern: int) -> int: + m = len(pattern) + partial_match_table = _get_partial_match_table(pattern) + j = 0 + for i in range(len(s)): + while j >= 0 and s[i] != pattern[j]: + j = partial_match_table[j] + j += 1 + if j == m: + return i - m + 1 + return -1 + + +def _get_partial_match_table(pattern: int) -> List[int]: + # Denote πᵏ(i) as π applied to i for k times, + # i.e., π²(i) = π(π(i)). + # Then we have the result: + # π(i) = πᵏ(i-1) + 1, + # where k is the smallest integer such that + # pattern[πᵏ(i-1)+1] == pattern[i]. + + # The value of π means the maximum length + # of proper prefix/suffix. + # The index of π means the length of the prefix + # considered for pattern. + # For example, π[2] means we are considering the first 2 characters + # of the pattern. + # If π[2] == 1, it means for the prefix of the pattern, P[0]P[1], + # it has a maximum length proper prefix of 1, which is also the + # suffix of P[0]P[1]. + # We also add a π[0] == -1 for easier handling of boundary + # condition. + + m = len(pattern) + π = [0] * (m + 1) + π[0] = k = -1 # We use k here to represent πᵏ(i) + for i in range(1, m + 1): + while k >= 0 and pattern[k] != pattern[i - 1]: + k = π[k] + k += 1 + π[i] = k + return π + + +if __name__ == "__main__": + + s = "abc abcdab abcdabcdabde" + pattern = "bcdabd" + print(kmp(s, pattern), s.find(pattern)) + + s = "hello" + pattern = "ll" + print(kmp(s, pattern), s.find(pattern)) \ No newline at end of file diff --git a/python/34_kmp/kmp_.py b/python/34_kmp/kmp_.py new file mode 100644 index 00000000..17fbedc7 --- /dev/null +++ b/python/34_kmp/kmp_.py @@ -0,0 +1,83 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + + +def kmp(main, pattern): + """ + kmp字符串匹配 + :param main: + :param pattern: + :return: + """ + assert type(main) is str and type(pattern) is str + + n, m = len(main), len(pattern) + + if m == 0: + return 0 + if n <= m: + return 0 if main == pattern else -1 + + # 求解next数组 + next = get_next(pattern) + + j = 0 + for i in range(n): + # 在pattern[:j]中,从长到短递归去找最长的和后缀子串匹配的前缀子串 + while j > 0 and main[i] != pattern[j]: + j = next[j-1] + 1 # 如果next[j-1] = -1,则要从起始字符取匹配 + + if main[i] == pattern[j]: + if j == m-1: + return i-m+1 + else: + j += 1 + return -1 + + +def get_next(pattern): + """ + next数组生成 + + 注意: + 理解的难点在于next[i]根据next[0], next[1]…… next[i-1]的求解 + next[i]的值依赖于前面的next数组的值,求解思路: + 1. 首先取出前一个最长的匹配的前缀子串,其下标就是next[i-1] + 2. 对比下一个字符,如果匹配,直接赋值next[i]为next[i-1]+1,因为i-1的时候已经是最长 + *3. 如果不匹配,需要递归去找次长的匹配的前缀子串,这里难理解的就是递归地方式,next[i-1] + 是i-1的最长匹配前缀子串的下标结尾,则 *next[next[i-1]]* 是其次长匹配前缀子串的下标 + 结尾 + *4. 递归的出口,就是在次长前缀子串的下一个字符和当前匹配 或 遇到-1,遇到-1则说明没找到任 + 何匹配的前缀子串,这时需要找pattern的第一个字符对比 + + ps: next[m-1]的数值其实没有任何意义,求解时可以不理。网上也有将next数组往右平移的做法。 + :param pattern: + :return: + """ + m = len(pattern) + next = [-1] * m + + next[0] = -1 + + # for i in range(1, m): + for i in range(1, m-1): + j = next[i-1] # 取i-1时匹配到的最长前缀子串 + while j != -1 and pattern[j+1] != pattern[i]: + j = next[j] # 次长的前缀子串的下标,即是next[next[i-1]] + + # 根据上面跳出while的条件,当j=-1时,需要比较pattern[0]和当前字符 + # 如果j!=-1,则pattern[j+1]和pattern[i]一定是相等的 + if pattern[j+1] == pattern[i]: # 如果接下来的字符也是匹配的,那i的最长前缀子串下标是next[i-1]+1 + j += 1 + next[i] = j + + return next + + +if __name__ == '__main__': + m_str = "aabbbbaaabbababbabbbabaaabb" + p_str = "abbabbbabaa" + + print('--- search ---') + print('[Built-in Functions] result:', m_str.find(p_str)) + print('[kmp] result:', kmp(m_str, p_str)) diff --git a/python/35_trie/trie.py b/python/35_trie/trie.py new file mode 100644 index 00000000..a1cf3eb6 --- /dev/null +++ b/python/35_trie/trie.py @@ -0,0 +1,42 @@ +""" + Author: Wenru Dong +""" + +class TrieNode: + def __init__(self, data: str): + self._data = data + self._children = [None] * 26 + self._is_ending_char = False + + +class Trie: + def __init__(self): + self._root = TrieNode("/") + + def insert(self, text: str) -> None: + node = self._root + for index, char in map(lambda x: (ord(x) - ord("a"), x), text): + if not node._children[index]: + node._children[index] = TrieNode(char) + node = node._children[index] + node._is_ending_char = True + + def find(self, pattern: str) -> bool: + node = self._root + for index in map(lambda x: ord(x) - ord("a"), pattern): + if not node._children[index]: return False + node = node._children[index] + return node._is_ending_char + + +if __name__ == "__main__": + + strs = ["how", "hi", "her", "hello", "so", "see"] + trie = Trie() + for s in strs: + trie.insert(s) + + for s in strs: + print(trie.find(s)) + + print(trie.find("swift")) \ No newline at end of file diff --git a/python/35_trie/trie_.py b/python/35_trie/trie_.py new file mode 100644 index 00000000..51b56c30 --- /dev/null +++ b/python/35_trie/trie_.py @@ -0,0 +1,175 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from queue import Queue +import pygraphviz as pgv + +OUTPUT_PATH = 'E:/' + + +class Node: + def __init__(self, c): + self.data = c + self.is_ending_char = False + # 使用有序数组,降低空间消耗,支持更多字符 + self.children = [] + + def insert_child(self, c): + self._insert_child(Node(c)) + + def _insert_child(self, node): + """ + 插入一个子节点 + :param c: + :return: + """ + v = ord(node.data) + idx = self._find_insert_idx(v) + length = len(self.children) + + if idx == length: + self.children.append(node) + else: + self.children.append(None) + for i in range(length, idx, -1): + self.children[i] = self.children[i-1] + self.children[idx] = node + + def has_child(self, c): + return True if self.get_child(c) is not None else False + + def get_child(self, c): + """ + 搜索子节点并返回 + :param c: + :return: + """ + start = 0 + end = len(self.children) - 1 + v = ord(c) + + while start <= end: + mid = (start + end)//2 + if v == ord(self.children[mid].data): + return self.children[mid] + elif v < ord(self.children[mid].data): + end = mid - 1 + else: + start = mid + 1 + # 找不到返回None + return None + + def _find_insert_idx(self, v): + """ + 二分查找,找到有序数组的插入位置 + :param v: + :return: + """ + start = 0 + end = len(self.children) - 1 + + while start <= end: + mid = (start + end)//2 + if v < ord(self.children[mid].data): + end = mid - 1 + else: + if mid + 1 == len(self.children) or v < ord(self.children[mid+1].data): + return mid + 1 + else: + start = mid + 1 + # v < self.children[0] + return 0 + + def __repr__(self): + return 'node value: {}'.format(self.data) + '\n' \ + + 'children:{}'.format([n.data for n in self.children]) + + +class Trie: + def __init__(self): + self.root = Node(None) + + def gen_tree(self, string_list): + """ + 创建trie树 + + 1. 遍历每个字符串的字符,从根节点开始,如果没有对应子节点,则创建 + 2. 每一个串的末尾节点标注为红色(is_ending_char) + :param string_list: + :return: + """ + for string in string_list: + n = self.root + for c in string: + if n.get_child(c) is None: + n.insert_child(c) + n = n.get_child(c) + n.is_ending_char = True + + def search(self, pattern): + """ + 搜索 + + 1. 遍历模式串的字符,从根节点开始搜索,如果途中子节点不存在,返回False + 2. 遍历完模式串,则说明模式串存在,再检查树中最后一个节点是否为红色,是 + 则返回True,否则False + :param pattern: + :return: + """ + assert type(pattern) is str and len(pattern) > 0 + + n = self.root + for c in pattern: + if n.get_child(c) is None: + return False + n = n.get_child(c) + + return True if n.is_ending_char is True else False + + def draw_img(self, img_name='Trie.png'): + """ + 画出trie树 + :param img_name: + :return: + """ + if self.root is None: + return + + tree = pgv.AGraph('graph foo {}', strict=False, directed=False) + + # root + nid = 0 + color = 'black' + tree.add_node(nid, color=color, label='None') + + q = Queue() + q.put((self.root, nid)) + while not q.empty(): + n, pid = q.get() + for c in n.children: + nid += 1 + q.put((c, nid)) + color = 'red' if c.is_ending_char is True else 'black' + tree.add_node(nid, color=color, label=c.data) + tree.add_edge(pid, nid) + + tree.graph_attr['epsilon'] = '0.01' + tree.layout('dot') + tree.draw(OUTPUT_PATH + img_name) + return True + + +if __name__ == '__main__': + string_list = ['abc', 'abd', 'abcc', 'accd', 'acml', 'P@trick', 'data', 'structure', 'algorithm'] + + print('--- gen trie ---') + print(string_list) + trie = Trie() + trie.gen_tree(string_list) + # trie.draw_img() + + print('\n') + print('--- search result ---') + search_string = ['a', 'ab', 'abc', 'abcc', 'abe', 'P@trick', 'P@tric', 'Patrick'] + for ss in search_string: + print('[pattern]: {}'.format(ss), '[result]: {}'.format(trie.search(ss))) diff --git a/python/36_ac_automata/ac_automata.py b/python/36_ac_automata/ac_automata.py new file mode 100644 index 00000000..a8922b49 --- /dev/null +++ b/python/36_ac_automata/ac_automata.py @@ -0,0 +1,80 @@ +""" + Aho-Corasick Algorithm + + Author: Wenru Dong +""" + +from collections import deque +from typing import List + +class ACNode: + def __init__(self, data: str): + self._data = data + self._children = [None] * 26 + self._is_ending_char = False + self._length = -1 + self._suffix = None + + +class ACAutomata: + def __init__(self): + self._root = ACNode("/") + + def _build_suffix_link(self) -> None: + q = deque() + q.append(self._root) + while q: + node = q.popleft() + for child in node._children: + if child: + if node == self._root: + child._suffix = self._root + else: + suffix = node._suffix + while suffix: + suffix_child = suffix._children[ord(child._data) - ord("a")] + if suffix_child: + child._suffix = suffix_child + break + suffix = suffix._suffix + if not suffix: + child._suffix = self._root + q.append(child) + + def _insert(self, text: str) -> None: + node = self._root + for index, char in map(lambda x: (ord(x) - ord("a"), x), text): + if not node._children[index]: + node._children[index] = ACNode(char) + node = node._children[index] + node._is_ending_char = True + node._length = len(text) + + def insert(self, patterns: List[str]) -> None: + for pattern in patterns: + self._insert(pattern) + self._build_suffix_link() + + def match(self, text: str) -> None: + node = self._root + for i, char in enumerate(text): + index = ord(char) - ord("a") + while not node._children[index] and node != self._root: + node = node._suffix + node = node._children[index] + if not node: + node = self._root + tmp = node + while tmp != self._root: + if tmp._is_ending_char: + print(f"匹配起始下标{i - tmp._length + 1},长度{tmp._length}") + tmp = tmp._suffix + + +if __name__ == "__main__": + + patterns = ["at", "art", "oars", "soar"] + ac = ACAutomata() + ac.insert(patterns) + + ac.match("soarsoars") diff --git a/python/36_ac_automata/ac_automata_.py b/python/36_ac_automata/ac_automata_.py new file mode 100644 index 00000000..3d324ec1 --- /dev/null +++ b/python/36_ac_automata/ac_automata_.py @@ -0,0 +1,88 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from trie_ import Node, Trie +from queue import Queue + + +class ACNode(Node): + def __init__(self, c: str): + super(ACNode, self).__init__(c) + self.fail = None + self.length = 0 + + def insert_child(self, c: str): + self._insert_child(ACNode(c)) + + +class ACTrie(Trie): + def __init__(self): + self.root = ACNode(None) + + +def ac_automata(main: str, ac_trie: ACTrie) -> list: + root = ac_trie.root + build_failure_pointer(ac_trie) + + ret = [] + p = root + for i, c in enumerate(main): + while p != root and not p.has_child(c): + p = p.fail + + if p.has_child(c): # a char matched, try to find all potential pattern matched + q = p.get_child(c) + while q != root: + if q.is_ending_char: + ret.append((i-q.length+1, i)) + # ret.append(main[i-q.length+1:i+1]) + q = q.fail + p = p.get_child(c) + + return ret + + +def build_failure_pointer(ac_trie: ACTrie) -> None: + root = ac_trie.root + + # queue: [(node, node.length) ....] + node_queue = Queue() + node_queue.put((root, root.length)) + + root.fail = None + while not node_queue.empty(): + p, length = node_queue.get() + for pc in p.children: + pc.length = length + 1 + if p == root: + pc.fail = root + else: + q = p.fail + # same as kmp + while q != root and not q.has_child(pc.data): + q = q.fail + + # cases now: + # 1. q == root + # 2. q != root and q.has_child(pc.data) + if q.has_child(pc.data): + pc.fail = q.get_child(pc.data) + else: + pc.fail = root + node_queue.put((pc, pc.length)) + + +if __name__ == '__main__': + ac_trie = ACTrie() + ac_trie.gen_tree(['fuck', 'shit', 'TMD', '傻叉']) + + print('--- ac automata ---') + m_str = 'fuck you, what is that shit, TMD你就是个傻叉傻叉傻叉叉' + print('original str : {}'.format(m_str)) + + filter_range_list = ac_automata(m_str, ac_trie) + str_filtered = m_str + for start, end in filter_range_list: + str_filtered = str_filtered.replace(str_filtered[start:end+1], '*'*(end+1-start)) + + print('after filtered: {}'.format(str_filtered)) diff --git a/python/38_divide_and_conquer/merge_sort_counting.py b/python/38_divide_and_conquer/merge_sort_counting.py new file mode 100644 index 00000000..b036746f --- /dev/null +++ b/python/38_divide_and_conquer/merge_sort_counting.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +inversion_num = 0 + + +def merge_sort_counting(nums, start, end): + if start >= end: + return + + mid = (start + end)//2 + merge_sort_counting(nums, start, mid) + merge_sort_counting(nums, mid+1, end) + merge(nums, start, mid, end) + + +def merge(nums, start, mid, end): + global inversion_num + i = start + j = mid+1 + tmp = [] + while i <= mid and j <= end: + if nums[i] <= nums[j]: + inversion_num += j - mid - 1 + tmp.append(nums[i]) + i += 1 + else: + tmp.append(nums[j]) + j += 1 + + while i <= mid: + # 这时nums[i]的逆序数是整个nums[mid+1: end+1]的长度 + inversion_num += end - mid + tmp.append(nums[i]) + i += 1 + + while j <= end: + tmp.append(nums[j]) + j += 1 + + nums[start: end+1] = tmp + + +if __name__ == '__main__': + print('--- count inversion number using merge sort ---') + # nums = [5, 0, 4, 2, 3, 1, 6, 8, 7] + nums = [5, 0, 4, 2, 3, 1, 3, 3, 3, 6, 8, 7] + print('nums : {}'.format(nums)) + merge_sort_counting(nums, 0, len(nums)-1) + print('sorted: {}'.format(nums)) + print('inversion number: {}'.format(inversion_num)) diff --git a/python/39_back_track/01_bag.py b/python/39_back_track/01_bag.py new file mode 100644 index 00000000..53c77316 --- /dev/null +++ b/python/39_back_track/01_bag.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List + +# 背包选取的物品列表 +picks = [] +picks_with_max_value = [] + + +def bag(capacity: int, cur_weight: int, items_info: List, pick_idx: int): + """ + 回溯法解01背包,穷举 + :param capacity: 背包容量 + :param cur_weight: 背包当前重量 + :param items_info: 物品的重量和价值信息 + :param pick_idx: 当前物品的索引 + :return: + """ + # 考察完所有物品,或者在中途已经装满 + if pick_idx >= len(items_info) or cur_weight == capacity: + global picks_with_max_value + if get_value(items_info, picks) > \ + get_value(items_info, picks_with_max_value): + picks_with_max_value = picks.copy() + else: + item_weight = items_info[pick_idx][0] + if cur_weight + item_weight <= capacity: # 选 + picks[pick_idx] = 1 + bag(capacity, cur_weight + item_weight, items_info, pick_idx + 1) + + picks[pick_idx] = 0 # 不选 + bag(capacity, cur_weight, items_info, pick_idx + 1) + + +def get_value(items_info: List, pick_items: List): + values = [_[1] for _ in items_info] + return sum([a*b for a, b in zip(values, pick_items)]) + + +if __name__ == '__main__': + # [(weight, value), ...] + items_info = [(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)] + capacity = 8 + + print('--- items info ---') + print(items_info) + + print('\n--- capacity ---') + print(capacity) + + picks = [0] * len(items_info) + bag(capacity, 0, items_info, 0) + + print('\n--- picks ---') + print(picks_with_max_value) + + print('\n--- value ---') + print(get_value(items_info, picks_with_max_value)) diff --git a/python/39_back_track/eight_queens.py b/python/39_back_track/eight_queens.py new file mode 100644 index 00000000..a25b7284 --- /dev/null +++ b/python/39_back_track/eight_queens.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +# 棋盘尺寸 +BOARD_SIZE = 8 + +solution_count = 0 +queen_list = [0] * BOARD_SIZE + + +def eight_queens(cur_column: int): + """ + 输出所有符合要求的八皇后序列 + 用一个长度为8的数组代表棋盘的列,数组的数字则为当前列上皇后所在的行数 + :return: + """ + if cur_column >= BOARD_SIZE: + global solution_count + solution_count += 1 + # 解 + print(queen_list) + else: + for i in range(BOARD_SIZE): + if is_valid_pos(cur_column, i): + queen_list[cur_column] = i + eight_queens(cur_column + 1) + + +def is_valid_pos(cur_column: int, pos: int) -> bool: + """ + 因为采取的是每列放置1个皇后的做法 + 所以检查的时候不必检查列的合法性,只需要检查行和对角 + 1. 行:检查数组在下标为cur_column之前的元素是否已存在pos + 2. 对角:检查数组在下标为cur_column之前的元素,其行的间距pos - QUEEN_LIST[i] + 和列的间距cur_column - i是否一致 + :param cur_column: + :param pos: + :return: + """ + i = 0 + while i < cur_column: + # 同行 + if queen_list[i] == pos: + return False + # 对角线 + if cur_column - i == abs(pos - queen_list[i]): + return False + i += 1 + return True + + +if __name__ == '__main__': + print('--- eight queens sequence ---') + eight_queens(0) + + print('\n--- solution count ---') + print(solution_count) diff --git a/python/39_back_track/permutations.py b/python/39_back_track/permutations.py new file mode 100644 index 00000000..b02a8766 --- /dev/null +++ b/python/39_back_track/permutations.py @@ -0,0 +1,42 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List + +permutations_list = [] # 全局变量,用于记录每个输出 + + +def permutations(nums: List, n: int, pick_count: int): + """ + 从nums选取n个数的全排列 + + 回溯法,用一个栈记录当前路径信息 + 当满足n==0时,说明栈中的数已足够,输出并终止遍历 + :param nums: + :param n: + :param pick_count: + :return: + """ + if n == 0: + print(permutations_list) + else: + for i in range(len(nums) - pick_count): + permutations_list[pick_count] = nums[i] + nums[i], nums[len(nums) - pick_count - 1] = nums[len(nums) - pick_count - 1], nums[i] + permutations(nums, n-1, pick_count+1) + nums[i], nums[len(nums) - pick_count - 1] = nums[len(nums) - pick_count - 1], nums[i] + + +if __name__ == '__main__': + nums = [1, 2, 3, 4] + n = 3 + print('--- list ---') + print(nums) + + print('\n--- pick num ---') + print(n) + + print('\n--- permutation list ---') + permutations_list = [0] * n + permutations(nums, n, 0) + diff --git a/python/39_back_track/regex.py b/python/39_back_track/regex.py new file mode 100644 index 00000000..001cac71 --- /dev/null +++ b/python/39_back_track/regex.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +is_match = False + + +def rmatch(r_idx: int, m_idx: int, regex: str, main: str): + global is_match + if is_match: + return + + if r_idx >= len(regex): # 正则串全部匹配好了 + is_match = True + return + + if m_idx >= len(main) and r_idx < len(regex): # 正则串没匹配完,但是主串已经没得匹配了 + is_match = False + return + + if regex[r_idx] == '*': # * 匹配1个或多个任意字符,递归搜索每一种情况 + for i in range(m_idx, len(main)): + rmatch(r_idx+1, i+1, regex, main) + elif regex[r_idx] == '?': # ? 匹配0个或1个任意字符,两种情况 + rmatch(r_idx+1, m_idx+1, regex, main) + rmatch(r_idx+1, m_idx, regex, main) + else: # 非特殊字符需要精确匹配 + if regex[r_idx] == main[m_idx]: + rmatch(r_idx+1, m_idx+1, regex, main) + + +if __name__ == '__main__': + regex = 'ab*eee?d' + main = 'abcdsadfkjlekjoiwjiojieeecd' + rmatch(0, 0, regex, main) + print(is_match) diff --git a/python/39_backtracking/backtracking.py b/python/39_backtracking/backtracking.py new file mode 100644 index 00000000..6f3059ac --- /dev/null +++ b/python/39_backtracking/backtracking.py @@ -0,0 +1,25 @@ +""" + Author: Wenru Dong +""" + +from typing import List + +def eight_queens() -> None: + solutions = [] + + def backtracking(queens_at_column: List[int], index_sums: List[int], index_diffs: List[int]) -> None: + row = len(queens_at_column) + if row == 8: + solutions.append(queens_at_column) + return + for col in range(8): + if col in queens_at_column or row + col in index_sums or row - col in index_diffs: continue + backtracking(queens_at_column + [col], index_sums + [row + col], index_diffs + [row - col]) + + backtracking([], [], []) + print(*(" " + " ".join("*" * i + "Q" + "*" * (8 - i - 1) + "\n" for i in solution) for solution in solutions), sep="\n") + + +if __name__ == "__main__": + + eight_queens() diff --git a/python/40_dynamic_programming/01_bag.py b/python/40_dynamic_programming/01_bag.py new file mode 100644 index 00000000..4c5d1b38 --- /dev/null +++ b/python/40_dynamic_programming/01_bag.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List, Tuple + + +def bag(items_info: List[int], capacity: int) -> int: + """ + 固定容量的背包,计算能装进背包的物品组合的最大重量 + + :param items_info: 每个物品的重量 + :param capacity: 背包容量 + :return: 最大装载重量 + """ + n = len(items_info) + memo = [[-1]*(capacity+1) for i in range(n)] + memo[0][0] = 1 + if items_info[0] <= capacity: + memo[0][items_info[0]] = 1 + + for i in range(1, n): + for cur_weight in range(capacity+1): + if memo[i-1][cur_weight] != -1: + memo[i][cur_weight] = memo[i-1][cur_weight] # 不选 + if cur_weight + items_info[i] <= capacity: # 选 + memo[i][cur_weight + items_info[i]] = 1 + + for w in range(capacity, -1, -1): + if memo[-1][w] != -1: + return w + + +def bag_with_max_value(items_info: List[Tuple[int, int]], capacity: int) -> int: + """ + 固定容量的背包,计算能装进背包的物品组合的最大价值 + + :param items_info: 物品的重量和价值 + :param capacity: 背包容量 + :return: 最大装载价值 + """ + n = len(items_info) + memo = [[-1]*(capacity+1) for i in range(n)] + memo[0][0] = 0 + if items_info[0][0] <= capacity: + memo[0][items_info[0][0]] = items_info[0][1] + + for i in range(1, n): + for cur_weight in range(capacity+1): + if memo[i-1][cur_weight] != -1: + memo[i][cur_weight] = memo[i-1][cur_weight] + if cur_weight + items_info[i][0] <= capacity: + memo[i][cur_weight + items_info[i][0]] = max(memo[i][cur_weight + items_info[i][0]], + memo[i-1][cur_weight] + items_info[i][1]) + return max(memo[-1]) + + +if __name__ == '__main__': + # [weight, ...] + items_info = [2, 2, 4, 6, 3] + capacity = 9 + print(bag(items_info, capacity)) + + # [(weight, value), ...] + items_info = [(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)] + capacity = 8 + print(bag_with_max_value(items_info, capacity)) diff --git a/python/40_dynamic_programming/knapsack.py b/python/40_dynamic_programming/knapsack.py new file mode 100644 index 00000000..7a71272d --- /dev/null +++ b/python/40_dynamic_programming/knapsack.py @@ -0,0 +1,30 @@ +""" + Author: Wenru Dong +""" + +from typing import List + +def knapsack01(weights: List[int], values: List[int], capacity: int) -> int: + # Denote the state as (i, c), where i is the stage number, + # and c is the capacity available. Denote f(i, c) to be the + # maximum value when the capacity available is c, and Item 0 + # to Item i-1 are to be packed. + # The goal is to find f(n-1, W), where W is the total capacity. + # Then the DP functional equation is: + # f(i, c) = max(xᵢvᵢ + f(i-1, c-xᵢwᵢ)), xᵢ ∈ D, i ≥ 0, + # f(-1, c) = 0, 0 ≤ c ≤ W, + # where + # / {0}, if wᵢ > c + # D = D(i, c) = + # \ {0, 1}, if wᵢ ≤ c + + prev = [0] * (capacity + 1) + for w, v in zip(weights, values): + prev = [c >= w and max(prev[c], prev[c-w] + v) for c in range(capacity + 1)] + return prev[-1] + + +if __name__ == "__main__": + # To find the maximum weight that can be packed, + # set values equal to the weights + print(knapsack01([2, 2, 4, 6, 3], [2, 2, 4, 6, 3], 9)) diff --git a/python/40_dynamic_programming/yh_triangle.py b/python/40_dynamic_programming/yh_triangle.py new file mode 100644 index 00000000..a652eddf --- /dev/null +++ b/python/40_dynamic_programming/yh_triangle.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List + +Layer_nums = List[int] + + +def yh_triangle(nums: List[Layer_nums]) -> int: + """ + 从根节点开始向下走,过程中经过的节点,只需存储经过它时最小的路径和 + :param nums: + :return: + """ + assert len(nums) > 0 + n = len(nums) # 层数 + memo = [[0]*n for i in range(n)] + memo[0][0] = nums[0][0] + + for i in range(1, n): + for j in range(i+1): + # 每一层首尾两个数字,只有一条路径可以到达 + if j == 0: + memo[i][j] = memo[i-1][j] + nums[i][j] + elif j == i: + memo[i][j] = memo[i-1][j-1] + nums[i][j] + else: + memo[i][j] = min(memo[i-1][j-1] + nums[i][j], memo[i-1][j] + nums[i][j]) + return min(memo[n-1]) + + +def yh_triangle_space_optimization(nums: List[Layer_nums]) -> int: + assert len(nums) > 0 + n = len(nums) + memo = [0] * n + memo[0] = nums[0][0] + + for i in range(1, n): + for j in range(i, -1, -1): + if j == i: + memo[j] = memo[j-1] + nums[i][j] + elif j == 0: + memo[j] = memo[j] + nums[i][j] + else: + memo[j] = min(memo[j-1] + nums[i][j], memo[j] + nums[i][j]) + return min(memo) + + +def yh_triangle_bottom_up(nums: List[Layer_nums]) -> int: + assert len(nums) > 0 + n = len(nums) + memo = nums[-1].copy() + + for i in range(n-1, 0, -1): + for j in range(i): + memo[j] = min(memo[j] + nums[i-1][j], memo[j+1] + nums[i-1][j]) + return memo[0] + + +if __name__ == '__main__': + nums = [[3], [2, 6], [5, 4, 2], [6, 0, 3, 2]] + print(yh_triangle(nums)) + print(yh_triangle_space_optimization(nums)) + print(yh_triangle_bottom_up(nums)) diff --git a/python/41_dynamic_programming/coins_problem.py b/python/41_dynamic_programming/coins_problem.py new file mode 100644 index 00000000..32543476 --- /dev/null +++ b/python/41_dynamic_programming/coins_problem.py @@ -0,0 +1,45 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List + + +def coins_dp(values: List[int], target: int) -> int: + # memo[i]表示target为i的时候,所需的最少硬币数 + memo = [0] * (target+1) + # 0元的时候为0个 + memo[0] = 0 + + for i in range(1, target+1): + min_num = 999999 + # 对于values中的所有n + # memo[i]为 min(memo[i-n1], memo[i-n2], ...) + 1 + for n in values: + if i >= n: + min_num = min(min_num, 1 + memo[i-n]) + else: # values中的数值要从小到大排序 + break + memo[i] = min_num + + # print(memo) + return memo[-1] + + +min_num = 999999 +def coins_backtracking(values: List[int], target: int, cur_value: int, coins_count: int): + if cur_value == target: + global min_num + min_num = min(coins_count, min_num) + else: + for n in values: + if cur_value + n <= target: + coins_backtracking(values, target, cur_value+n, coins_count+1) + + +if __name__ == '__main__': + values = [1, 3, 5] + target = 23 + print(coins_dp(values, target)) + coins_backtracking(values, target, 0, 0) + print(min_num) + diff --git a/python/41_dynamic_programming/min_dist.py b/python/41_dynamic_programming/min_dist.py new file mode 100644 index 00000000..2b8e60ae --- /dev/null +++ b/python/41_dynamic_programming/min_dist.py @@ -0,0 +1,39 @@ +""" + Author: Wenru Dong +""" + +from typing import List +from itertools import accumulate + +def min_dist(weights: List[List[int]]) -> int: + """Find the minimum weight path from the weights matrix.""" + m, n = len(weights), len(weights[0]) + table = [[0] * n for _ in range(m)] + # table[i][j] is the minimum distance (weight) when + # there are i vertical moves and j horizontal moves + # left. + table[0] = list(accumulate(reversed(weights[-1]))) + for i, v in enumerate(accumulate(row[-1] for row in reversed(weights))): + table[i][0] = v + for i in range(1, m): + for j in range(1, n): + table[i][j] = weights[~i][~j] + min(table[i - 1][j], table[i][j - 1]) + return table[-1][-1] + + +def min_dist_recur(weights: List[List[int]]) -> int: + m, n = len(weights), len(weights[0]) + table = [[0] * n for _ in range(m)] + def min_dist_to(i: int, j: int) -> int: + if i == j == 0: return weights[0][0] + if table[i][j]: return table[i][j] + min_left = float("inf") if j - 1 < 0 else min_dist_to(i, j - 1) + min_up = float("inf") if i - 1 < 0 else min_dist_to(i - 1, j) + return weights[i][j] + min(min_left, min_up) + return min_dist_to(m - 1, n - 1) + + +if __name__ == "__main__": + weights = [[1, 3, 5, 9], [2, 1, 3, 4], [5, 2, 6, 7], [6, 8, 4, 3]] + print(min_dist(weights)) + print(min_dist_recur(weights)) diff --git a/python/42_dynamic_programming/longest_increasing_subsequence.py b/python/42_dynamic_programming/longest_increasing_subsequence.py new file mode 100644 index 00000000..634dc9ba --- /dev/null +++ b/python/42_dynamic_programming/longest_increasing_subsequence.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List + + +def longest_increasing_subsequence(nums: List[int]) -> int: + """ + 最长子上升序列的一种DP解法,从回溯解法转化,思路类似于有限物品的背包问题 + 每一次决策都算出当前可能的lis的长度,重复子问题合并,合并策略是lis的末尾元素最小 + 时间复杂度:O(n^2) + 空间复杂度:O(n^2),可优化至O(n) + + 没leetcode上的参考答案高效,提供另一种思路作为参考 + https://leetcode.com/problems/longest-increasing-subsequence/solution/ + :param nums: + :return: + """ + if not nums: + return 0 + + n = len(nums) + # memo[i][j] 表示第i次决策,长度为j的lis的 最小的 末尾元素数值 + # 每次决策都根据上次决策的所有可能转化,空间上可以类似背包优化为O(n) + memo = [[-1] * (n+1) for _ in range(n)] + + # 第一列全赋值为0,表示每次决策都不选任何数 + for i in range(n): + memo[i][0] = 0 + # 第一次决策选数组中的第一个数 + memo[0][1] = nums[0] + + for i in range(1, n): + for j in range(1, n+1): + # case 1: 长度为j的lis在上次决策后存在,nums[i]比长度为j-1的lis末尾元素大 + if memo[i-1][j] != -1 and nums[i] > memo[i-1][j-1]: + memo[i][j] = min(nums[i], memo[i-1][j]) + + # case 2: 长度为j的lis在上次决策后存在,nums[i]比长度为j-1的lis末尾元素小/等 + if memo[i-1][j] != -1 and nums[i] <= memo[i-1][j-1]: + memo[i][j] = memo[i-1][j] + + if memo[i-1][j] == -1: + # case 3: 长度为j的lis不存在,nums[i]比长度为j-1的lis末尾元素大 + if nums[i] > memo[i-1][j-1]: + memo[i][j] = nums[i] + # case 4: 长度为j的lis不存在,nums[i]比长度为j-1的lis末尾元素小/等 + break + + for i in range(n, -1, -1): + if memo[-1][i] != -1: + return i + + +if __name__ == '__main__': + # 要求输入的都是大于0的正整数(可优化至支持任意整数) + nums = [2, 9, 3, 6, 5, 1, 7] + print(longest_increasing_subsequence(nums)) diff --git a/python/42_dynamic_programming/min_edit_dist.py b/python/42_dynamic_programming/min_edit_dist.py new file mode 100644 index 00000000..f98efb8e --- /dev/null +++ b/python/42_dynamic_programming/min_edit_dist.py @@ -0,0 +1,59 @@ +""" + Author: Wenru Dong +""" + +def levenshtein_dp(s: str, t: str) -> int: + m, n = len(s), len(t) + table = [[0] * (n) for _ in range(m)] + + for i in range(n): + if s[0] == t[i]: + table[0][i] = i - 0 + elif i != 0: + table[0][i] = table[0][i - 1] + 1 + else: + table[0][i] = 1 + + for i in range(m): + if s[i] == t[0]: + table[i][0] = i - 0 + elif i != 0: + table[i][0] = table[i - 1][0] + 1 + else: + table[i][0] = 1 + + for i in range(1, m): + for j in range(1, n): + table[i][j] = min(1 + table[i - 1][j], 1 + table[i][j - 1], int(s[i] != t[j]) + table[i - 1][j - 1]) + + print(table) + return table[-1][-1] + + +def common_substring_dp(s: str, t: str) -> int: + m, n = len(s), len(t) + table = [[0] * (n + 1) for _ in range(m + 1)] + for i in range(1, m + 1): + for j in range(1, n + 1): + table[i][j] = max(table[i - 1][j], table[i][j - 1], int(s[i - 1] == t[j - 1]) + table[i - 1][j - 1]) + return table[-1][-1] + + +if __name__ == "__main__": + s = "mitcmu" + t = "mtacnu" + + print(levenshtein_dp(s, t)) + print(common_substring_dp(s, t)) + + s = "kitten" + t = "sitting" + + print(levenshtein_dp(s, t)) + print(common_substring_dp(s, t)) + + s = "flaw" + t = "lawn" + + print(levenshtein_dp(s, t)) + print(common_substring_dp(s, t)) \ No newline at end of file diff --git a/python/43_topological_sorting/topological_sorting.py b/python/43_topological_sorting/topological_sorting.py new file mode 100644 index 00000000..8971740c --- /dev/null +++ b/python/43_topological_sorting/topological_sorting.py @@ -0,0 +1,63 @@ +""" + Author: Wenru Dong +""" + +from collections import deque +from itertools import filterfalse + +class Graph: + def __init__(self, num_vertices: int): + self._num_vertices = num_vertices + self._adjacency = [[] for _ in range(num_vertices)] + + def add_edge(self, s: int, t: int) -> None: + self._adjacency[s].append(t) + + def tsort_by_kahn(self) -> None: + in_degree = [0] * self._num_vertices + for v in range(self._num_vertices): + if len(self._adjacency[v]): + for neighbour in self._adjacency[v]: + in_degree[neighbour] += 1 + q = deque(filterfalse(lambda x: in_degree[x], range(self._num_vertices))) + while q: + v = q.popleft() + print(f"{v} -> ", end="") + for neighbour in self._adjacency[v]: + in_degree[neighbour] -= 1 + if not in_degree[neighbour]: + q.append(neighbour) + print("\b\b\b ") + + def tsort_by_dfs(self) -> None: + inverse_adjacency = [[] for _ in range(self._num_vertices)] + for v in range(self._num_vertices): + if len(self._adjacency[v]): + for neighbour in self._adjacency[v]: + inverse_adjacency[neighbour].append(v) + visited = [False] * self._num_vertices + + def dfs(vertex: int) -> None: + if len(inverse_adjacency[vertex]): + for v in inverse_adjacency[vertex]: + if not visited[v]: + visited[v] = True + dfs(v) + print(f"{vertex} -> ", end="") + + for v in range(self._num_vertices): + if not visited[v]: + visited[v] = True + dfs(v) + + print("\b\b\b ") + + +if __name__ == "__main__": + + dag = Graph(4) + dag.add_edge(1, 0) + dag.add_edge(2, 1) + dag.add_edge(1, 3) + dag.tsort_by_kahn() + dag.tsort_by_dfs() diff --git a/python/44_shortest_path/dijkstra.py b/python/44_shortest_path/dijkstra.py new file mode 100644 index 00000000..e56a5af6 --- /dev/null +++ b/python/44_shortest_path/dijkstra.py @@ -0,0 +1,127 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from typing import List, Generator +import heapq + + +class Graph: + def __init__(self, vertex_count: int) -> None: + self.adj = [[] for _ in range(vertex_count)] + + def add_edge(self, s: int, t: int, w: int) -> None: + edge = Edge(s, t, w) + self.adj[s].append(edge) + + def __len__(self) -> int: + return len(self.adj) + + +class Vertex: + def __init__(self, v: int, dist: int) -> None: + self.id = v + self.dist = dist + + def __gt__(self, other) -> bool: + return self.dist > other.dist + + def __repr__(self) -> str: + return str((self.id, self.dist)) + + +class Edge: + def __init__(self, source: int, target: int, weight: int) -> None: + self.s = source + self.t = target + self.w = weight + + +class VertexPriorityQueue: + def __init__(self) -> None: + self.vertices = [] + + def get(self) -> Vertex: + return heapq.heappop(self.vertices) + + def put(self, v: Vertex) -> None: + self.vertices.append(v) + self.update_priority() + + def empty(self) -> bool: + return len(self.vertices) == 0 + + def update_priority(self) -> None: + heapq.heapify(self.vertices) + + def __repr__(self) -> str: + return str(self.vertices) + + +def dijkstra(g: Graph, s: int, t: int) -> int: + size = len(g) + + pq = VertexPriorityQueue() # 节点队列 + in_queue = [False] * size # 已入队标记 + vertices = [ # 需要随时更新离s的最短距离的节点列表 + Vertex(v, float('inf')) for v in range(size) + ] + predecessor = [-1] * size # 先驱 + + vertices[s].dist = 0 + pq.put(vertices[s]) + in_queue[s] = True + + while not pq.empty(): + v = pq.get() + if v.id == t: + break + for edge in g.adj[v.id]: + if v.dist + edge.w < vertices[edge.t].dist: + # 当修改了pq中的元素的优先级后: + # 1. 有入队操作:触发了pq的堆化,此后出队可以取到优先级最高的顶点 + # 2. 无入队操作:此后出队取到的顶点可能不是优先级最高的,会有bug + # 为确保正确,需要手动更新一次 + vertices[edge.t].dist = v.dist + edge.w + predecessor[edge.t] = v.id + pq.update_priority() # 更新堆结构 + if not in_queue[edge.t]: + pq.put(vertices[edge.t]) + in_queue[edge.t] = True + + for n in print_path(s, t, predecessor): + if n == t: + print(t) + else: + print(n, end=' -> ') + return vertices[t].dist + + +def print_path(s: int, t: int, p: List[int]) -> Generator[int, None, None]: + if t == s: + yield s + else: + yield from print_path(s, p[t], p) + yield t + + +if __name__ == '__main__': + g = Graph(6) + g.add_edge(0, 1, 10) + g.add_edge(0, 4, 15) + g.add_edge(1, 2, 15) + g.add_edge(1, 3, 2) + g.add_edge(2, 5, 5) + g.add_edge(3, 2, 1) + g.add_edge(3, 5, 12) + g.add_edge(4, 5, 10) + print(dijkstra(g, 0, 5)) + + # 下面这个用例可以暴露更新队列元素优先级的问题 + # g = Graph(4) + # g.add_edge(0, 1, 18) + # g.add_edge(0, 2, 3) + # g.add_edge(2, 1, 1) + # g.add_edge(1, 3, 5) + # g.add_edge(2, 3, 8) + # g.add_edge(0, 3, 15) + # print(dijkstra(g, 0, 3)) diff --git a/python/44_shortest_path/shortest_path.py b/python/44_shortest_path/shortest_path.py new file mode 100644 index 00000000..57387bd0 --- /dev/null +++ b/python/44_shortest_path/shortest_path.py @@ -0,0 +1,64 @@ +""" + Dijkstra algorithm + + Author: Wenru Dong +""" + +from dataclasses import dataclass +from queue import PriorityQueue + +@dataclass +class Edge: + start_id: int + end_id: int + weight: int + +@dataclass(order=True) +class Vertex: + distance_to_start = float("inf") + vertex_id: int + +class Graph: + def __init__(self, num_vertices: int): + self._num_vertices = num_vertices + self._adjacency = [[] for _ in range(num_vertices)] + + def add_edge(self, from_vertex: int, to_vertex: int, weight: int) -> None: + self._adjacency[from_vertex].append(Edge(from_vertex, to_vertex, weight)) + + def dijkstra(self, from_vertex: int, to_vertex: int) -> None: + vertices = [Vertex(i) for i in range(self._num_vertices)] + vertices[from_vertex].distance_to_start = 0 + visited = [False] * self._num_vertices + predecessor = [-1] * self._num_vertices + q = PriorityQueue() + q.put(vertices[from_vertex]) + visited[from_vertex] = True + while not q.empty(): + min_vertex = q.get() + if min_vertex.vertex_id == to_vertex: + break + for edge in self._adjacency[min_vertex.vertex_id]: + next_vertex = vertices[edge.end_id] + if min_vertex.distance_to_start + edge.weight < next_vertex.distance_to_start: + next_vertex.distance_to_start = min_vertex.distance_to_start + edge.weight + predecessor[next_vertex.vertex_id] = min_vertex.vertex_id + if not visited[next_vertex.vertex_id]: + q.put(next_vertex) + visited[next_vertex.vertex_id] = True + + path = lambda x: path(predecessor[x]) + [str(x)] if from_vertex != x else [str(from_vertex)] + print("->".join(path(to_vertex))) + + +if __name__ == "__main__": + graph = Graph(6) + graph.add_edge(0, 1, 10) + graph.add_edge(0, 4, 15) + graph.add_edge(1, 2, 15) + graph.add_edge(1, 3, 2) + graph.add_edge(2, 5, 5) + graph.add_edge(3, 2, 1) + graph.add_edge(3, 5, 12) + graph.add_edge(4, 5, 10) + graph.dijkstra(0, 5) diff --git a/python/45_bitmap/bitmap.py b/python/45_bitmap/bitmap.py new file mode 100644 index 00000000..1022b742 --- /dev/null +++ b/python/45_bitmap/bitmap.py @@ -0,0 +1,30 @@ +""" + Author: Wenru Dong +""" + +from typing import Optional + +class Bitmap: + def __init__(self, num_bits: int): + self._num_bits = num_bits + self._bytes = bytearray(num_bits // 8 + 1) + + def setbit(self, k: int) -> None: + if k > self._num_bits or k < 1: return + self._bytes[k // 8] |= (1 << k % 8) + + def getbit(self, k: int) -> Optional[bool]: + if k > self._num_bits or k < 1: return + return self._bytes[k // 8] & (1 << k % 8) != 0 + + +if __name__ == "__main__": + bitmap = Bitmap(10) + bitmap.setbit(1) + bitmap.setbit(3) + bitmap.setbit(6) + bitmap.setbit(7) + bitmap.setbit(8) + + for i in range(1, 11): + print(bitmap.getbit(i)) \ No newline at end of file diff --git a/python/array.py b/python/array.py new file mode 100644 index 00000000..31f3c4de --- /dev/null +++ b/python/array.py @@ -0,0 +1,70 @@ +# 1.数组的插入、删除、按照下标随机访问操作; +# 2.数组中的数据类型是Int +# +# Author:Lee + +class Array(): + + def __init__(self): + '''数组类初始化方法.''' + self.__data = [] # 数据存储List + + def find(self, index): + '''数组的查找方法. + + 参数: + index:将要查找的数据的下标 + + 返回: + 如果查找成功,则返回找到的数据 + 如果查找失败,则返回False + ''' + if index > len(self.__data) or index < 0: + return False + else: + return self.__data[index] + + def delete(self, index): + '''数组的删除方法. + + 参数: + index:将要删除的数据的下标 + + 返回: + 如果删除成功,则返回True + 如果删除失败,则返回False + ''' + if index > len(self.__data) or index < 0: + return False + else: + self.__data.pop(index) + return True + + def insert(self, index, value): + '''数组插入数据操作. + + 参数: + index:将要插入的下标 + value:将要插入的数据 + + 返回: + 如果插入成功,则返回True + 如果插入失败,则返回False + ''' + if index > len(self.__data) or index < 0: + return False + else: + self.__data.insert(index, value) + return True + + def insertToTail(self, value): + '''直接在数组尾部插入数据. + + 参数: + value:将要插入的数据 + ''' + self.__data.append(value) + + def printAll(self): + '''打印当前数组所有数据''' + print(self.__data) diff --git a/rust/05_array/main.rs b/rust/05_array/main.rs new file mode 100644 index 00000000..a8525658 --- /dev/null +++ b/rust/05_array/main.rs @@ -0,0 +1,72 @@ +struct NewArray { + array: Vec, + count: i32, +} + +impl NewArray { + fn new(capacity: usize) -> NewArray { + NewArray { array: Vec::with_capacity(capacity), count: 0 } + } + + fn find(&self, index: usize) -> i32 { + if index > self.count as usize { return -1; } + self.array[index] + } + + fn insert(&mut self, index: usize, value: i32) -> bool { + let array_count = self.count as usize; + if index > array_count || array_count == self.array.capacity() { return false; } + + if index == array_count { + self.array.push(value); + } else { + let tmp_arr = self.array.clone(); + + self.array = Vec::with_capacity(self.array.capacity()); + for i in 0..index { + self.array.push(tmp_arr[i]); + } + + self.array.push(value); + for i in index..array_count { + self.array.push(tmp_arr[i]); + } + } + + self.count += 1; + true + } + + fn remove(&mut self, index: usize) -> i32 { + if index >= self.count as usize { return -1; } + + let result = self.array[index]; + let tmp_arr = self.array.clone(); + + self.array = Vec::with_capacity(self.array.capacity()); + for i in 0..index { + self.array.push(tmp_arr[i]); + } + + for i in index+1..self.count as usize { + self.array.push(tmp_arr[i]); + } + + self.count -=1; + result + } +} + +fn main() { + let mut new_array = NewArray::new(10); + assert_eq!(new_array.insert(0, 3), true); + assert_eq!(new_array.insert(1, 2), true); + assert_eq!(new_array.insert(2, 8), true); + assert_eq!(new_array.insert(0, 9), true); + assert_eq!(new_array.insert(5, 7), false); + assert_eq!(new_array.insert(4, 5), true); + assert_eq!(new_array.find(3), 8); + assert_eq!(new_array.find(12), -1); + assert_eq!(new_array.remove(1), 3); + assert_eq!(new_array.remove(9), -1); +} diff --git a/rust/07_linkedlist/linked_list_cycle.rs b/rust/07_linkedlist/linked_list_cycle.rs new file mode 100644 index 00000000..2c73d08a --- /dev/null +++ b/rust/07_linkedlist/linked_list_cycle.rs @@ -0,0 +1,18 @@ +use super::util::linked_list::{ListNode, to_list}; + +pub fn has_cycle(head: Option>) -> bool { + let mut fast_p = &head; + let mut slow_p = &head; + + while fast_p.is_some() && fast_p.as_ref().unwrap().next.is_some() { + slow_p = &slow_p.as_ref().unwrap().next; + fast_p = &fast_p.as_ref().unwrap().next.as_ref().unwrap().next; + + if slow_p == fast_p { return true; } + } + false +} + +fn main() { + println!("{:?}", has_cycle(to_list(vec![1, 2, 3, 4, 5]))); +} diff --git a/rust/07_linkedlist/merge_two_sorted_lists.rs b/rust/07_linkedlist/merge_two_sorted_lists.rs new file mode 100644 index 00000000..7754f4f7 --- /dev/null +++ b/rust/07_linkedlist/merge_two_sorted_lists.rs @@ -0,0 +1,24 @@ +use super::util::linked_list::{ListNode, to_list}; + +pub fn merge_two_lists(l1: Option>, l2: Option>) -> Option> { + match (l1, l2) { + (Some(node1), None) => Some(node1), + (None, Some(node2)) => Some(node2), + (Some(mut node1), Some(mut node2)) => { + if node1.val < node2.val { + let n = node1.next.take(); + node1.next = Solution::merge_two_lists(n, Some(node2)); + Some(node1) + } else { + let n = node2.next.take(); + node2.next = Solution::merge_two_lists(Some(node1), n); + Some(node2) + } + }, + _ => None, + } +} + +fn main() { + println!("{:?}", merge_two_lists(to_list(vec![1, 3, 4]), to_list(vec![1, 2, 4]))); +} diff --git a/rust/07_linkedlist/middle_of_the_linked_list.rs b/rust/07_linkedlist/middle_of_the_linked_list.rs new file mode 100644 index 00000000..9ef2ee58 --- /dev/null +++ b/rust/07_linkedlist/middle_of_the_linked_list.rs @@ -0,0 +1,16 @@ +use super::util::linked_list::{ListNode, to_list}; + +pub fn middle_node(head: Option>) -> Option> { + let mut fast_p = &head; + let mut slow_p = &head; + + while fast_p.is_some() && fast_p.as_ref().unwrap().next.is_some() { + slow_p = &slow_p.as_ref().unwrap().next; + fast_p = &fast_p.as_ref().unwrap().next.as_ref().unwrap().next; + } + slow_p.clone() +} + +fn main() { + println!("{:?}", middle_node(to_list(vec![1, 3, 4]))); +} diff --git a/rust/07_linkedlist/remove_nth_node_from_end_of_list.rs b/rust/07_linkedlist/remove_nth_node_from_end_of_list.rs new file mode 100644 index 00000000..cdf6d716 --- /dev/null +++ b/rust/07_linkedlist/remove_nth_node_from_end_of_list.rs @@ -0,0 +1,28 @@ +use super::util::linked_list::{ListNode, to_list}; + +pub fn remove_nth_from_end(head: Option>, n: i32) -> Option> { + let mut dummy = Some(Box::new(ListNode { val: 0, next: head })); + let mut cur = &mut dummy; + let mut length = 0; + + while let Some(_node) = cur.as_mut() { + cur = &mut cur.as_mut().unwrap().next; + if let Some(_inner_node) = cur { length += 1; } + } + + let mut new_cur = dummy.as_mut(); + let idx = length - n; + + for _ in 0..idx { + new_cur = new_cur.unwrap().next.as_mut(); + } + + let next = new_cur.as_mut().unwrap().next.as_mut().unwrap().next.take(); + new_cur.as_mut().unwrap().next = next; + + dummy.unwrap().next +} + +fn main() { + println!("{:?}", remove_nth_from_end(to_list(vec![1, 3, 4]))); +} diff --git a/rust/07_linkedlist/reverse_linked_list.rs b/rust/07_linkedlist/reverse_linked_list.rs new file mode 100644 index 00000000..fc753972 --- /dev/null +++ b/rust/07_linkedlist/reverse_linked_list.rs @@ -0,0 +1,20 @@ +use super::util::linked_list::{ListNode, to_list}; + +pub fn reverse_list(head: Option>) -> Option> { + let mut prev = None; + let mut curr = head; + + while let Some(mut boxed_node) = curr.take() { + let next = boxed_node.next.take(); + boxed_node.next = prev.take(); + + prev = Some(boxed_node); + curr = next; + } + + prev +} + +fn main() { + println!("{:?}", reverse_list(to_list(vec![1, 2, 3, 4, 5]))); +} diff --git a/rust/07_linkedlist/util/linked_list.rs b/rust/07_linkedlist/util/linked_list.rs new file mode 100644 index 00000000..36b8b056 --- /dev/null +++ b/rust/07_linkedlist/util/linked_list.rs @@ -0,0 +1,32 @@ +// Definition for singly-linked list. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct ListNode { + pub val: i32, + pub next: Option> +} + +impl ListNode { + #[inline] + fn new(val: i32) -> Self { + ListNode { + next: None, + val + } + } +} + +pub fn to_list(vec: Vec) -> Option> { + let mut current = None; + for &v in vec.iter().rev() { + let mut node = ListNode::new(v); + node.next = current; + current = Some(Box::new(node)); + } + current +} + +#[macro_export] +macro_rules! linked { + ($($e:expr),*) => {to_list(vec![$($e.to_owned()), *])}; + ($($e:expr,)*) => {to_list(vec![$($e.to_owned()), *])}; +} diff --git a/rust/08_stack/simple_browser.rs b/rust/08_stack/simple_browser.rs new file mode 100644 index 00000000..f9deecd7 --- /dev/null +++ b/rust/08_stack/simple_browser.rs @@ -0,0 +1,83 @@ +mod stack_based_on_linked_list; + +use stack_based_on_linked_list::{LinkedListStack}; + +#[derive(Hash, Eq, PartialEq, Debug, Default, Clone)] +struct Browser { + forward_stack: LinkedListStack, + back_stack: LinkedListStack, + current_page: String, +} + +impl Browser { + fn new() -> Self { + Default::default() + } + + fn open(&mut self, url: &String) { + if !self.current_page.is_empty() { + self.back_stack.push(self.current_page.clone()); + self.forward_stack.clear(); + } + self.show_url(&url, "Open".to_string()); + } + + fn go_back(&mut self) -> String { + if self.can_go_back() { + self.forward_stack.push(self.current_page.clone()); + let back_url = self.back_stack.pop(); + self.show_url(&back_url, "Back".to_string()); + return back_url; + } + + println!("Can not go back, there is no page behind."); + "-1".to_string() + } + + fn go_forward(&mut self) -> String { + if self.can_go_forward() { + self.back_stack.push(self.current_page.clone()); + let forward_url = self.forward_stack.pop(); + self.show_url(&forward_url, "Forward".to_string()); + return forward_url; + } + + println!("Can not go forward, there is no page behind."); + "-1".to_string() + } + + fn can_go_back(&self) -> bool { + !self.back_stack.is_empty() + } + + fn can_go_forward(&self) -> bool { + !self.forward_stack.is_empty() + } + + fn show_url(&mut self, url: &String, prefix: String) { + self.current_page = url.to_string(); + println!("{:?} page == {:?}", prefix, url); + } + + fn check_current_page(&self) { + println!("current page == {:?}", self.current_page); + } +} +fn main() { + let mut browser = Browser::new(); + browser.open(&"http://www.baidu.com".to_string()); + browser.open(&"http://news.baidu.com/".to_string()); + browser.open(&"http://news.baidu.com/ent".to_string()); + browser.go_back(); + browser.go_back(); + browser.go_forward(); + browser.open(&"http://www.qq.com".to_string()); + browser.go_forward(); + browser.go_back(); + browser.go_forward(); + browser.go_back(); + browser.go_back(); + browser.go_back(); + browser.go_back(); + browser.check_current_page(); +} diff --git a/rust/08_stack/stack_based_on_array.rs b/rust/08_stack/stack_based_on_array.rs new file mode 100644 index 00000000..9c7322a8 --- /dev/null +++ b/rust/08_stack/stack_based_on_array.rs @@ -0,0 +1,47 @@ +#[derive(Debug)] +struct ArrayStack { + data: Vec, + top: i32, +} + +impl ArrayStack { + fn new() -> Self { + ArrayStack { data: Vec::with_capacity(32), top: -1 } + } + + fn push(&mut self, x: i32) { + self.top += 1; + if self.top == self.data.capacity() as i32 { + let tmp_arr = self.data.clone(); + self.data = Vec::with_capacity(self.data.capacity() * 2); + for d in tmp_arr.into_iter() { + self.data.push(d); + } + } + self.data.push(x); + } + + fn pop(&mut self) { + if self.is_empty() { return; } + self.top -= 1; + self.data.pop(); + } + + fn top(&self) -> i32 { + if self.is_empty() { return -1; } + *self.data.last().unwrap() + } + + fn is_empty(&self) -> bool { + if self.top < 0 { true } else { false } + } +} + +fn main() { + let mut stack = ArrayStack::new(); + stack.push(-2); + stack.push(0); + stack.push(-3); + stack.pop(); + println!("{:?}", stack.top()); +} diff --git a/rust/08_stack/stack_based_on_linked_list.rs b/rust/08_stack/stack_based_on_linked_list.rs new file mode 100644 index 00000000..15e56c4e --- /dev/null +++ b/rust/08_stack/stack_based_on_linked_list.rs @@ -0,0 +1,64 @@ +#[derive(Hash, Eq, PartialEq, Debug, Default, Clone)] +pub struct ListNode { + val: String, + next: Option>, +} + +#[derive(Hash, Eq, PartialEq, Debug, Default, Clone)] +pub struct LinkedListStack { + node: Option>, +} + +impl ListNode { + fn new(val: String) -> Self { + ListNode { val: val, next: None } + } +} + +impl LinkedListStack { + pub fn new() -> Self { + Default::default() + } + + pub fn push(&mut self, x: String) { + let mut n = ListNode::new(x); + n.next = self.node.clone(); + self.node = Some(Box::new(n)); + } + + pub fn pop(&mut self) -> String { + if self.is_empty() { return "-1".to_string(); } + + let val = self.node.as_ref().unwrap().val.clone(); + self.node = self.node.as_mut().unwrap().next.take(); + val.to_string() + } + + pub fn print_all(&mut self) { + let mut list = String::from(""); + + while let Some(n) = self.node.as_mut() { + list.push_str(&(n.val).to_string()); + list.push_str("-->"); + self.node = n.next.take(); + } + println!("{:?}", list); + } + + pub fn clear(&mut self) { + self.node = None; + } + + pub fn is_empty(&self) -> bool { + if self.node.is_none() { true } else { false } + } +} +// +// fn main() { +// let mut stack = LinkedListStack::new(); +// stack.push("https://www.baidu.com".to_string()); +// stack.push("https://www.google.com".to_string()); +// stack.pop(); +// stack.push("https://twitter.com".to_string()); +// stack.print_all(); +// } diff --git a/rust/09_queue/array_queue.rs b/rust/09_queue/array_queue.rs new file mode 100644 index 00000000..e4621b4e --- /dev/null +++ b/rust/09_queue/array_queue.rs @@ -0,0 +1,65 @@ +#[derive(Debug)] +struct ArrayQueue { + queue: Vec, + head: i32, + tail: i32, +} + +impl ArrayQueue { + fn new(n: usize) -> Self { + ArrayQueue { + queue: Vec::with_capacity(n), + head: 0, + tail: 0, + } + } + + fn enqueue(&mut self, num: i32) -> bool { + let c = self.queue.capacity() as i32; + // queue is full + if self.head == 0 && self.tail == c { return false; } + if self.tail == c { + for i in 0..(self.tail-self.head) as usize { + self.queue[i] = self.queue[self.head as usize + i]; + } + self.tail -= self.head; + self.head = 0; + self.queue[self.tail as usize] = num; + } else { + self.queue.push(num); + } + + self.tail += 1; + true + } + + fn dequeue(&mut self) -> i32 { + if self.head == self.tail { return -1; } + + let shift = self.queue[self.head as usize]; + self.head += 1; + shift + } + + fn print_all(&self) { + let mut s = String::from(""); + for i in self.head..self.tail { + s.push(self.queue[i as usize] as u8 as char); + s.push_str("->"); + } + println!("{:?}", s); + } +} + +fn main() { + let mut queue = ArrayQueue::new(3); + queue.enqueue(2); + queue.enqueue(2); + queue.enqueue(2); + queue.enqueue(2); + queue.dequeue(); + queue.dequeue(); + queue.enqueue(4); + queue.dequeue(); + queue.print_all(); +} diff --git a/rust/09_queue/circle_queue.rs b/rust/09_queue/circle_queue.rs new file mode 100644 index 00000000..d263be9d --- /dev/null +++ b/rust/09_queue/circle_queue.rs @@ -0,0 +1,55 @@ +#[derive(Debug)] +struct CircleQueue { + queue: Vec, + head: i32, + tail: i32, + n: i32, +} + +impl CircleQueue { + fn new(n: i32) -> Self { + CircleQueue { + queue: vec![-1; n as usize], + head: 0, + tail: 0, + n: n, + } + } + + fn enqueue(&mut self, num: i32) -> bool { + if (self.tail + 1) % self.n == self.head { return false; } + self.queue[self.tail as usize] = num; + self.tail = (self.tail + 1) % self.n; + true + } + + fn dequeue(&mut self) -> i32 { + if self.head == self.tail { return -1; } + + let shift = self.queue[self.head as usize]; + self.head = (self.head + 1) % self.n; + shift + } + + fn print_all(&self) { + let mut s = String::from(""); + for i in self.head..self.tail { + s.push(self.queue[i as usize] as u8 as char); + s.push_str("->"); + } + println!("{:?}", s); + } +} + +fn main() { + let mut queue = CircleQueue::new(10); + queue.enqueue(2); + queue.enqueue(2); + queue.enqueue(2); + queue.enqueue(2); + queue.dequeue(); + queue.dequeue(); + queue.enqueue(4); + queue.dequeue(); + queue.print_all(); +} diff --git a/rust/09_queue/linked_list_queue.rs b/rust/09_queue/linked_list_queue.rs new file mode 100644 index 00000000..1eaf025c --- /dev/null +++ b/rust/09_queue/linked_list_queue.rs @@ -0,0 +1,69 @@ +#![feature(box_into_raw_non_null)] + +use std::ptr::NonNull; + +#[derive(Debug)] +pub struct LinkedListQueue { + head: Option>, + tail: Option>, +} + +pub struct Node { + next: Option>, + element: i32, +} + +impl Node { + fn new(element: i32) -> Self { + Node { + next: None, + element, + } + } + + fn into_element(self: Box) -> i32 { + self.element + } +} + +impl LinkedListQueue { + pub fn new() -> Self { + LinkedListQueue { + head: None, + tail: None, + } + } + + pub fn dequeue(&mut self) -> i32 { + self.head.map(|node| unsafe { + let node = Box::from_raw(node.as_ptr()); + self.head = node.next; + node + }).map(Node::into_element).unwrap() + } + + pub fn enqueue(&mut self, elt: i32) { + let mut node = Box::new(Node::new(elt)); + unsafe { + node.next = None; + let node = Some(Box::into_raw_non_null(node)); + + match self.tail { + None => self.head = node, + Some(tail) => (*tail.as_ptr()).next = node, + } + + self.tail = node; + } + } +} + +fn main() { + let mut m = LinkedListQueue::new(); + m.enqueue(4); + m.enqueue(4); + m.enqueue(4); + m.dequeue(); + m.dequeue(); + println!("{:?}", m); +} diff --git a/rust/11_sorts/bubble_sort.rs b/rust/11_sorts/bubble_sort.rs new file mode 100644 index 00000000..71669952 --- /dev/null +++ b/rust/11_sorts/bubble_sort.rs @@ -0,0 +1,29 @@ +/// 冒泡排序 +/// 时间复杂度:O(n2),原地排序算法, 稳定排序算法 +// 冒泡排序 +fn bubble_sort(mut nums: Vec) -> Vec { + if nums.is_empty() { return vec![]; } + + let n = nums.len(); + for i in 0..n { + // 提前退出标志 + let mut swap = false; + for j in 0..n-i-1 { + if nums[j] > nums[j+1] { + // 此次冒泡有数据交换 + swap = true; + let tmp = nums[j]; + nums[j] = nums[j+1]; + nums[j+1] = tmp; + } + } + // 若没有数据交换,提前退出 + if !swap { break; } + } + nums +} + +fn main() { + let nums = vec![4, 5, 6, 1, 2, 3]; + println!("{:?}", bubble_sort(nums)); +} diff --git a/rust/11_sorts/insertion_sort.rs b/rust/11_sorts/insertion_sort.rs new file mode 100644 index 00000000..98a5abf2 --- /dev/null +++ b/rust/11_sorts/insertion_sort.rs @@ -0,0 +1,27 @@ +/// 插入排序 +/// 时间复杂度:O(n2),原地排序算法, 稳定排序算法 +// 插入排序 +fn insertion_sort(mut nums: Vec) -> Vec { + if nums.is_empty() { return vec![]; } + + for i in 1..nums.len() { + let value = nums[i]; + let mut j = (i - 1) as i32; + // 查找要插入的位置并移动数据 + while j >= 0 { + if nums[j as usize] > value { + nums[(j+1) as usize] = nums[j as usize]; + } else { + break; + } + j -= 1; + } + nums[(j+1) as usize] = value; + } + nums +} + +fn main() { + let nums = vec![4, 5, 6, 1, 2, 3]; + println!("{:?}", insert_on_sort(nums)); +} diff --git a/rust/11_sorts/selection_sort.rs b/rust/11_sorts/selection_sort.rs new file mode 100644 index 00000000..ae66a265 --- /dev/null +++ b/rust/11_sorts/selection_sort.rs @@ -0,0 +1,26 @@ +/// 选择排序 +/// 时间复杂度:O(n2),原地排序算法, 不稳定排序算法 +// 选择排序 +fn selection_sort(mut nums: Vec) -> Vec { + if nums.is_empty() { return vec![]; } + + for i in 0..nums.len() { + let mut mini_index = i; + // 查找最小 index + for j in i+1..nums.len() { + if nums[j] < nums[mini_index] { + mini_index = j; + } + } + // 交换数据 + let tmp = nums[i]; + nums[i] = nums[mini_index]; + nums[mini_index] = tmp; + } + nums +} + +fn main() { + let nums = vec![4, 5, 6, 1, 2, 3]; + println!("{:?}", selection_sort(nums)); +} diff --git a/rust/12_sorts/kth_largest.rs b/rust/12_sorts/kth_largest.rs new file mode 100644 index 00000000..1f196b60 --- /dev/null +++ b/rust/12_sorts/kth_largest.rs @@ -0,0 +1,44 @@ +pub fn kth_largest(mut nums: Vec, k: i32) -> Option { + if nums.is_empty() || k >= nums.len() as i32 { return None; } + + let end = nums.len() - 1; + let k = k as usize; + // 分区点 + let mut pivot = partition(&mut nums, 0, end); + while pivot + 1 != k { + if k > pivot + 1 { + pivot = partition(&mut nums, pivot + 1, end); + } else { + pivot = partition(&mut nums, 0, pivot - 1); + } + } + + Some(nums[pivot]) +} + +fn partition(nums: &mut Vec, start: usize, end: usize) -> usize { + let pivot = nums[end]; + let mut i = start; + for j in start..end { + if nums[j] >= pivot { // if nums[j] <= pivot then swap, search kth smallest + swap(nums, i, j); + i += 1; + } + } + + swap(nums, i, end); + i +} + +fn swap(nums: &mut Vec, i: usize, j: usize) { + if i == j { return; } + + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; +} + +fn main() { + let nums = vec![8, 10, 2, 3, 6,1, 5]; + println!("{:?}", kth_largest(nums, 3)); +} diff --git a/rust/12_sorts/merge_sort.rs b/rust/12_sorts/merge_sort.rs new file mode 100644 index 00000000..dc8f8989 --- /dev/null +++ b/rust/12_sorts/merge_sort.rs @@ -0,0 +1,57 @@ +/// 归并排序 +/// 时间复杂度 O(nlogn), 空间复杂度:O(n), 稳定排序 +// 归并排序 +pub fn merge_sort(mut nums: Vec) -> Vec { + if nums.is_empty() { return nums; } + + let n = nums.len() - 1; + merge_sort_internally(&mut nums, 0, n); + nums +} + +fn merge_sort_internally(nums: &mut Vec, start: usize, end: usize) { + if start >= end { return; } + + let middle = start + (end - start) / 2; + merge_sort_internally(nums, start, middle); + merge_sort_internally(nums, middle+1, end); + + // merge two array + merge(nums, start, middle, end); +} + +fn merge(nums: &mut Vec, start: usize, middle: usize, end: usize) { + let mut i = start; + let mut j = middle + 1; + let mut tmp = vec![]; + while i <= middle && j <= end { + if nums[i] <= nums[j] { + tmp.push(nums[i]); + i += 1; + } else { + tmp.push(nums[j]); + j += 1; + } + } + + let mut s = i; + let mut r = middle; + if j <= end { + s = j; + r = end; + } + + while s <= r { + tmp.push(nums[s]); + s += 1; + } + + for i in 0..=(end-start) { + nums[start+i] = tmp[i]; + } +} + +fn main() { + let nums = vec![8, 10, 2, 3, 6,1, 5]; + println!("{:?}", merge_sort(nums)); +} diff --git a/rust/12_sorts/quick_sort.rs b/rust/12_sorts/quick_sort.rs new file mode 100644 index 00000000..a3bde13e --- /dev/null +++ b/rust/12_sorts/quick_sort.rs @@ -0,0 +1,48 @@ +/// 快排 +/// 时间复杂度:O(nlogn), 空间复杂度: O(1), 不稳定排序 +// 快排 +pub fn quick_sort(mut nums: Vec) -> Vec { + if nums.is_empty() { return nums; } + + let n = nums.len() - 1; + quick_sort_internally(&mut nums, 0, n); + nums +} + +fn quick_sort_internally(nums: &mut Vec, start: usize, end: usize) { + if start >= end { return; } + + // 分区点 + let pivot = partition(nums, start, end); + if pivot != 0 { + quick_sort_internally(nums, start, pivot-1); + } + quick_sort_internally(nums, pivot+1, end); +} + +fn partition(nums: &mut Vec, start: usize, end: usize) -> usize { + let pivot = nums[end]; + let mut i = start; + for j in start..end { + if nums[j] < pivot { + swap(nums, i, j); + i += 1; + } + } + + swap(nums, i, end); + i +} + +fn swap(nums: &mut Vec, i: usize, j: usize) { + if i == j { return; } + + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; +} + +fn main() { + let nums = vec![8, 10, 2, 3, 6,1, 5]; + println!("{:?}", quick_sort(nums)); +} diff --git a/rust/13_sorts/bucket_sort.rs b/rust/13_sorts/bucket_sort.rs new file mode 100644 index 00000000..4746558a --- /dev/null +++ b/rust/13_sorts/bucket_sort.rs @@ -0,0 +1,80 @@ +/// 桶排序 +/// 时间复杂度:O(n), 稳定排序 +// 桶排序 +pub fn bucket_sort(mut nums: Vec, step: i32) -> Vec { + let (mut min, mut max) = (nums[0], nums[0]); + for i in 0..nums.len() { + if min > nums[i] { min = nums[i]; } + if max < nums[i] { max = nums[i]; } + } + + // 设置需要的桶的数量 + let bucket_num = (max - min) / step + 1; + let mut bucket_list: Vec> = vec![vec![]; bucket_num as usize]; + // 将 nums 数组中元素分别装入桶中 + for i in 0..nums.len() { + // 计算桶 index + let index = (nums[i] - min) / step; + bucket_list[index as usize].push(nums[i]); + } + + let mut index = 0; + for i in 0..bucket_num { + let bucket = &bucket_list[i as usize]; + // 对每个桶中元素使用快排进行排序 + let new_bucket = quick_sort(bucket.to_vec()); + // 将已经排序好的桶中元素拷贝到 nums 数组中 + for num in new_bucket.into_iter() { + nums[index as usize] = num; + index += 1; + } + } + nums +} + +pub fn quick_sort(mut nums: Vec) -> Vec { + if nums.is_empty() { return nums; } + + let n = nums.len() - 1; + quick_sort_internally(&mut nums, 0, n); + nums +} + +fn quick_sort_internally(nums: &mut Vec, start: usize, end: usize) { + if start >= end { return; } + + // 分区点 + let pivot = partition(nums, start, end); + if pivot != 0 { + quick_sort_internally(nums, start, pivot-1); + } + quick_sort_internally(nums, pivot+1, end); +} + +fn partition(nums: &mut Vec, start: usize, end: usize) -> usize { + let pivot = nums[end]; + let mut i = start; + for j in start..end { + if nums[j] < pivot { + swap(nums, i, j); + i += 1; + } + } + + swap(nums, i, end); + i +} + +fn swap(nums: &mut Vec, i: usize, j: usize) { + if i == j { return; } + + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; +} + +fn main() { + let nums = vec![2, 5, 3, 0, 2, 3, 0, 3]; + let m = bucket_sort(nums, 3); + println!("{:?}", m); +} diff --git a/rust/13_sorts/counting_sort.rs b/rust/13_sorts/counting_sort.rs new file mode 100644 index 00000000..cf8fc03b --- /dev/null +++ b/rust/13_sorts/counting_sort.rs @@ -0,0 +1,51 @@ +/// 计数排序 +/// 时间复杂度:O(n), 稳定排序 +// 计数排序 +pub fn counting_sort(mut nums: Vec) -> Vec { + if nums.len() <= 1 { return nums; } + + let nums_len = nums.len(); + // 获取最大数 + let mut max = nums[0]; + // 申请一个长度为 max + 1 的新数组 + let mut bucket = vec![0; (max+1) as usize]; + let mut tmp = vec![0; nums_len]; + + for i in 1..nums_len { + if max < nums[i] { max = nums[i]; } + } + + for i in 0..nums_len { + bucket[nums[i] as usize] += 1; + } + + // 对 bucket 中元素进行累加,针对 nums 数组中每个元素,小于等于这个元素的个数 + // 如,nums 数组中元素 3,小于等于 3 的个数为 7 个 + for i in 1..bucket.len() { + bucket[i] += bucket[i-1]; + } + + // 排序 + // 1. 申请一个与 nums 等长的数组,用于存储排序后的内容; + // 2. 对数组 nums 从后向前迭代 + // 1). 从 bucket 数组中取出下标为 nums 数组中当前元素的值,如 nums 中当前元素为3,则从 bucket + // 中取出下标为 3 的元素(即:在 nums 数组中元素 3 的位置应该为 7,index 为 6) + // 2). 将元素 3 存入临时数组,此时元素 3 的个数变为 6 个 + // 3). 依次类推,直到将所有元素存入临时数组 tmp 中,完成排序 + for i in (0..nums_len).rev() { + let index = bucket[nums[i] as usize] - 1; + tmp[index] = nums[i]; + bucket[nums[i] as usize] -= 1; + } + + for i in 0..tmp.len() { + nums[i] = tmp[i]; + } + nums +} + +fn main() { + let nums = vec![2, 5, 3, 0, 2, 3, 0, 3]; + let m = counting_sort(nums); + println!("{:?}", m); +} diff --git a/rust/13_sorts/radix_sort.rs b/rust/13_sorts/radix_sort.rs new file mode 100644 index 00000000..0e2f067c --- /dev/null +++ b/rust/13_sorts/radix_sort.rs @@ -0,0 +1,58 @@ +/// 基数排序 +/// 时间复杂度:O(n), 稳定排序 +// 基数排序 +pub fn radix_sort(mut nums: Vec) -> Vec { + let max_bit = max_bit(&nums); + // 申请一个长度为 10 的桶 + let mut bucket = vec![0; 10]; + let mut tmp = vec![0; nums.len()]; + let mut radix = 1; + let nums_len = nums.len(); + + // 迭代 max_bit 次 + for _i in 0..max_bit { + // 每次比较前先将桶清空 + for j in 0..bucket.len() { bucket[j] = 0; } + + for j in 0..nums_len { + let index = ((nums[j] / radix) % 10) as usize; + bucket[index] += 1; + } + + for j in 1..bucket.len() { bucket[j] += bucket[j-1]; } + + // 对 nums 进行排序 + for j in (0..nums_len).rev() { + let index = ((nums[j] / radix) % 10) as usize; + tmp[(bucket[index]-1) as usize] = nums[j]; + bucket[index] -= 1; + } + + for j in 0..nums_len { + nums[j] = tmp[j]; + } + + radix *= 10; + } + nums +} + +fn max_bit(nums: &Vec) -> i32 { + let mut max = nums[0]; + let mut bit = 1; // 预防数据库中元素全部是 0 的情况 + for &num in nums.iter() { + if num > max { max = num; } + } + + while max >= 10 { + max = max / 10; + bit += 1; + } + bit +} + +fn main() { + // let nums = vec![1, 10, 100, 1000, 98, 67, 3, 28, 67, 888, 777]; + let nums = vec![13111111111, 13299999999, 13311111111, 13133333333, 13922222222, 13722222222]; + println!("{:?}", radix_sort(nums)); +} diff --git a/rust/13_sorts/sort_string.rs b/rust/13_sorts/sort_string.rs new file mode 100644 index 00000000..e08e6cdd --- /dev/null +++ b/rust/13_sorts/sort_string.rs @@ -0,0 +1,18 @@ +fn sort_string(s: String) -> Vec> { + let mut bucket: Vec> = vec![vec![]; 3]; + for ch in s.as_bytes() { + if ch as u8 >= 48 && ch as u8 <= 57 { + bucket[0].push(ch); + } else if ch as u8 >= 65 && ch as u8 <= 90 { + bucket[1].push(ch); + } else { + bucket[2].push(ch); + } + } + + bucket +} +fn main() { + let s = "DaFBCA789".to_string(); + println!("{:?}", sort_string(s)); +} diff --git a/rust/15_binary_search/binary_search.rs b/rust/15_binary_search/binary_search.rs new file mode 100644 index 00000000..cdbcad2d --- /dev/null +++ b/rust/15_binary_search/binary_search.rs @@ -0,0 +1,46 @@ +// 二分查找 +pub fn binary_search(nums: Vec, value: i32) -> i32 { + if nums.is_empty() { return -1; } + + let mut low = 0; + let mut high = nums.len() - 1; + + while low <= high { + let mid = low + ((high - low) >> 1); + if nums[mid] == value { return mid as i32; } + + if nums[mid] < value { + low = mid + 1; + } else { + high = mid -1; + } + } + -1 +} + +// 二分查找递归 +pub fn binary_search_recursion(nums: Vec, value: i32) -> i32 { + if nums.is_empty() { return -1; } + + _recursion(&nums, 0, nums.len()-1, value) +} + +fn _recursion(nums: &Vec, low: usize, high: usize, value: i32) -> i32 { + if low > high { return -1; } + + let mid = low + ((high - low) >> 1); + if nums[mid] == value { return mid as i32; } + + if nums[mid] < value { + return _recursion(nums, mid+1, high, value); + } else { + return _recursion(nums, low, mid-1, value); + } +} + +fn main() { + let nums1 = vec![8,11,19,23,27,33,45,55,67,98]; + let nums2 = vec![8,11,19,23,27,33,45,55,67,98]; + println!("{:?}", binary_search(nums1, 23)); + println!("{:?}", binary_search_recursion(nums2, 23)); +} diff --git a/rust/15_binary_search/sqrtx.rs b/rust/15_binary_search/sqrtx.rs new file mode 100644 index 00000000..fbfb6acb --- /dev/null +++ b/rust/15_binary_search/sqrtx.rs @@ -0,0 +1,28 @@ +// leetcode: https://leetcode.com/problems/sqrtx/ + +pub fn my_sqrt(x: i32, precision: f32) -> f32 { + if x == 0 || x == 1 { return x as f32; } + + let mut left = 0f32; + let mut right = x as f32; + let mut res = 0f32; + + while left <= right { + let mid: f32 = (right - left) / 2.0 + left; + + if (right - left).abs() < precision { return mid; } + + if mid > x as f32 / mid { + right = mid; + } else { + left = mid; + } + res = mid + } + + res +} + +fn main() { + println!("{:?}", my_sqrt(8, 0.000001)); +} diff --git a/rust/16_binary_search/binary_search.rs b/rust/16_binary_search/binary_search.rs new file mode 100644 index 00000000..f604aba4 --- /dev/null +++ b/rust/16_binary_search/binary_search.rs @@ -0,0 +1,93 @@ +// 查找第一个给定值的元素 +fn find_first_eq(nums: Vec, value: i32) -> i32 { + if nums.is_empty() { return -1; } + + let mut start = 0; + let mut end = nums.len() - 1; + + while start <= end { + let mid = start + ((end - start) >> 1); + if nums[mid] <= value { + if mid == 0 || (nums[mid] == value && nums[mid-1] != value) { return mid as i32; } + start = mid + 1; + } else { + end = mid - 1; + } + } + -1 +} + +// 查找最后一个给定值的元素 +fn find_last_eq(nums: Vec, value: i32) -> i32 { + if nums.is_empty() { return -1; } + + let mut start = 0; + let mut end = nums.len() - 1; + + while start <= end { + let mid = start + ((end - start) >> 1); + if nums[mid] <= value { + if mid == end || (nums[mid] == value && nums[mid+1] != value) { return mid as i32; } + start = mid + 1; + } else { + end = mid - 1; + } + } + -1 +} + +// 查找第一个大于等于给定值的元素 +fn find_first_ge(nums: Vec, value: i32) -> i32 { + if nums.is_empty() { return -1; } + + let mut start = 0; + let mut end = nums.len() - 1; + + while start <= end { + let mid = start + ((end - start) >> 1); + if nums[mid] < value { + start = mid + 1; + } else { + if mid == end || (nums[mid] >= value && nums[mid-1] < value) { return mid as i32; } + end = mid - 1; + } + } + -1 +} + +// 查找最后一个小于等于给定值的元素 +fn find_last_le(nums: Vec, value: i32) -> i32 { + if nums.is_empty() { return -1; } + + let mut start = 0; + let mut end = nums.len() - 1; + + while start <= end { + let mid = start + ((end - start) >> 1); + if nums[mid] <= value { + if mid == 0 || (nums[mid] <= value && nums[mid+1] > value) { return mid as i32; } + start = mid + 1; + } else { + end = mid - 1; + } + } + -1 +} + +fn main() { + let nums1 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; + let first_eq = find_first_eq(nums1, 8); + println!("{:?}", first_eq); + + let nums2 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; + let last_eq = find_last_eq(nums2, 8); + println!("{:?}", last_eq); + + let nums3 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; + let find_first_ge = find_first_ge(nums3, 5); + println!("{:?}", find_first_ge); + + let nums4 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18]; + let find_last_le = find_last_le(nums4, 17); + println!("{:?}", find_last_le); +} diff --git a/rust/16_binary_search/search_in_rotated_sorted_array.rs b/rust/16_binary_search/search_in_rotated_sorted_array.rs new file mode 100644 index 00000000..180f67b6 --- /dev/null +++ b/rust/16_binary_search/search_in_rotated_sorted_array.rs @@ -0,0 +1,35 @@ +// leetcode 33 Search in Rotated Sorted Array (https://leetcode.com/problems/search-in-rotated-sorted-array/) +pub fn search(nums: Vec, target: i32) -> i32 { + if nums.is_empty() { return -1; } + + let mut low = 0; + let mut high = nums.len() - 1; + + while low <= high { + let mid = low + ((high - low) >> 1); + if nums[mid] == target { return mid as i32; } + + // left is order + if nums[low] <= nums[mid] { + // target is in left array + if nums[low] <= target && target <= nums[mid] { + high = mid - 1; + } else { + low = mid + 1; + } + } else { + if nums[mid] <= target && target <= nums[high] { + low = mid + 1; + } else { + high = mid - 1; + } + } + } + -1 +} + +fn main() { + let nums = vec![4,5,6,7,0,1,2]; + let n = search(nums, 0); + println!("{:?}", n); +} diff --git a/rust/19_hash_table/hash_table.rs b/rust/19_hash_table/hash_table.rs new file mode 100644 index 00000000..601b641c --- /dev/null +++ b/rust/19_hash_table/hash_table.rs @@ -0,0 +1,56 @@ +#[derive(Debug,Default)] +pub struct MyHashTable<'a> { + table: Vec>, + capacity: usize, +} + +impl<'a> MyHashTable<'a> { + fn new() -> MyHashTable<'a> { + MyHashTable { + table: vec![None; 16], + capacity: 16, + } + } + + pub fn insert(&mut self, key: &'a str, value: &'a str) { + let pos = self.hash(key) as usize; + self.table[pos] = Some(value); + } + + pub fn get(&self, key: &'a str) -> Option<&'a str> { + let pos = self.hash(key) as usize; + self.table[pos] + } + + pub fn remove(&mut self, key: &'a str) -> Option<&'a str> { + let pos = self.hash(key) as usize; + let value = self.table[pos]; + self.table[pos] = None; + value + } + + fn hash(&self, key: &'a str) -> i32 { + let h = self.hash_code(key); + (h ^ (h >> 16)) & (self.capacity as i32 - 1) + } + + fn hash_code(&self, key: &'a str) -> i32 { + let mut hash = 0; + for ch in key.chars() { + hash += 31 * hash + ch as i32; + } + hash as i32 + } +} +fn main() { + let mut hash_table = MyHashTable::new(); + hash_table.insert("hello", "rust"); + println!("{:?}", hash_table); + hash_table.insert("hi", "C++"); + println!("{:?}", hash_table); + let m = hash_table.get("hello"); + println!("{:?}", m); + let n = hash_table.remove("hi"); + println!("{:?}", n); + println!("{:?}", hash_table); +} diff --git a/rust/23_binary_tree/inorder_traversal.rs b/rust/23_binary_tree/inorder_traversal.rs new file mode 100644 index 00000000..622b2a74 --- /dev/null +++ b/rust/23_binary_tree/inorder_traversal.rs @@ -0,0 +1,44 @@ +// leetcode: https://leetcode.com/problems/binary-tree-inorder-traversal/ +use super::util::tree::{TreeNode, to_tree}; + +// 中序遍历(Recursive Approach) +pub fn inorder_traversal(root: Option>>) -> Vec { + let mut result: Vec = vec![]; + if root.is_none() { return result; } + + _inorder(root, &mut result); + result +} + +fn _inorder(root: Option>>, result: &mut Vec) { + match root { + Some(node) => { + _inorder(node.borrow().left.clone(), result); + result.push(node.borrow().val); + _inorder(node.borrow().right.clone(), result); + }, + None => { return; } + } +} + +// 中序遍历(Iterating method using Stack) +pub fn inorder_traversal(root: Option>>) -> Vec { + let mut result = vec![]; + if root.is_none() { return result; } + + let mut stack: Vec>> = Vec::new(); + let mut r = root.clone(); + + while r.is_some() || !stack.is_empty() { + while let Some(node) = r { + stack.push(node.clone()); + r = node.borrow().left.clone(); + } + r = stack.pop(); + if let Some(node) = r { + result.push(node.borrow().val); + r = node.borrow().right.clone(); + } + } + result +} diff --git a/rust/23_binary_tree/level_order_traversal.rs b/rust/23_binary_tree/level_order_traversal.rs new file mode 100644 index 00000000..9fb8ae3b --- /dev/null +++ b/rust/23_binary_tree/level_order_traversal.rs @@ -0,0 +1,34 @@ +// leetcode https://leetcode.com/problems/binary-tree-level-order-traversal/ + +use super::util::tree::{TreeNode, to_tree}; +use std::collections::VecDeque; + +pub fn level_order(root: Option>>) -> Vec> { + let mut result: Vec> = vec![]; + if root.is_none() { return result; } + + let mut deque: VecDeque>>> = VecDeque::new(); + deque.push_back(root); + + while !deque.is_empty() { + let mut current_level = vec![]; + let mut added = false; + let level_size = deque.len(); + + for i in 0..level_size { + let n = deque.pop_front(); + if let Some(Some(node)) = n { + current_level.push(node.borrow().val); + added = true; + if node.borrow().left.is_some() { deque.push_back(node.borrow().left.clone()); } + if node.borrow().right.is_some() { deque.push_back(node.borrow().right.clone()); } + } + } + + if !added { break; } + + result.push(current_level); + } + + result +} diff --git a/rust/23_binary_tree/postorder_traversal.rs b/rust/23_binary_tree/postorder_traversal.rs new file mode 100644 index 00000000..b675f7b8 --- /dev/null +++ b/rust/23_binary_tree/postorder_traversal.rs @@ -0,0 +1,48 @@ +// https://leetcode.com/problems/binary-tree-postorder-traversal/ +use super::util::tree::{TreeNode, to_tree}; + +// 后续遍历(Recursive Approach) +pub fn postorder_traversal(root: Option>>) -> Vec { + let mut result: Vec = vec![]; + if root.is_none() { return result; } + + _postorder(root, &mut result); + result +} + +fn _postorder(root: Option>>, result: &mut Vec) { + match root { + Some(node) => { + _postorder(node.borrow().left.clone(), result); + _postorder(node.borrow().right.clone(), result); + result.push(node.borrow().val); + }, + None => { return; } + } +} + +// 后序遍历(Iterating method using Stack) +pub fn postorder_traversal(root: Option>>) -> Vec { + let mut result = vec![]; + if root.is_none() { return result; } + + let mut stack1: Vec>>> = Vec::new(); + let mut stack2: Vec>>> = Vec::new(); + stack1.push(root); + + while let Some(Some(node)) = stack1.pop() { + if node.borrow().left.is_some() { + stack1.push(node.borrow().left.clone()); + } + if node.borrow().right.is_some() { + stack1.push(node.borrow().right.clone()); + } + stack2.push(Some(node)); + } + + while let Some(Some(node)) = stack2.pop() { + result.push(node.borrow().val); + } + + result +} diff --git a/rust/23_binary_tree/preorder_traversal.rs b/rust/23_binary_tree/preorder_traversal.rs new file mode 100644 index 00000000..1b2d2a41 --- /dev/null +++ b/rust/23_binary_tree/preorder_traversal.rs @@ -0,0 +1,44 @@ +//leetcode: https://leetcode.com/problems/binary-tree-preorder-traversal/ +use super::util::tree::{TreeNode, to_tree}; + +// 前序遍历(Recursive Approach) +pub fn preorder_traversal(root: Option>>) -> Vec { + let mut result: Vec = vec![]; + if root.is_none() { return result; } + + _preorder(root, &mut result); + result +} + +fn _preorder(root: Option>>, result: &mut Vec) { + match root { + Some(node) => { + result.push(node.borrow().val); + _preorder(node.borrow().left.clone(), result); + _preorder(node.borrow().right.clone(), result); + }, + None => { return; } + } +} + +// 前序遍历(Iterating method using Stack) +pub fn inorder_traversal(root: Option>>) -> Vec { + let mut result = vec![]; + if root.is_none() { return result; } + + let mut stack: Vec>> = Vec::new(); + let mut r = root.clone(); + + while r.is_some() || !stack.is_empty() { + while let Some(node) = r { + result.push(node.borrow().val); + stack.push(node.clone()); + r = node.borrow().left.clone(); + } + r = stack.pop(); + if let Some(node) = r { + r = node.borrow().right.clone(); + } + } + result +} diff --git a/rust/23_binary_tree/util/tree.rs b/rust/23_binary_tree/util/tree.rs new file mode 100644 index 00000000..fc8c727e --- /dev/null +++ b/rust/23_binary_tree/util/tree.rs @@ -0,0 +1,57 @@ +use std::rc::Rc; +use std::cell::RefCell; + +#[derive(Debug, PartialEq, Eq)] +pub struct TreeNode { + pub val: i32, + pub left: Option>>, + pub right: Option>>, +} + +impl TreeNode { + #[inline] + pub fn new(val: i32) -> Self { + TreeNode { + val, + left: None, + right: None + } + } +} + +pub fn to_tree(vec: Vec>) -> Option>> { + use std::collections::VecDeque; + let head = Some(Rc::new(RefCell::new(TreeNode::new(vec[0].unwrap())))); + let mut queue = VecDeque::new(); + queue.push_back(head.as_ref().unwrap().clone()); + + for children in vec[1..].chunks(2) { + let parent = queue.pop_front().unwrap(); + if let Some(v) = children[0] { + parent.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(v)))); + queue.push_back(parent.borrow().left.as_ref().unwrap().clone()); + } + if children.len() > 1 { + if let Some(v) = children[1] { + parent.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(v)))); + queue.push_back(parent.borrow().right.as_ref().unwrap().clone()); + } + } + } + head +} + +#[macro_export] +macro_rules! tree { + () => { + None + }; + ($($e:expr),*) => { + { + let vec = vec![$(stringify!($e)), *]; + let vec = vec.into_iter().map(|v| v.parse::().ok()).collect::>(); + to_tree(vec) + } + }; + ($($e:expr,)*) => {(tree![$($e),*])}; +} diff --git a/rust/24_binary_tree/insert_in_binary_tree.rs b/rust/24_binary_tree/insert_in_binary_tree.rs new file mode 100644 index 00000000..c35099df --- /dev/null +++ b/rust/24_binary_tree/insert_in_binary_tree.rs @@ -0,0 +1,19 @@ +// https://leetcode.com/problems/insert-into-a-binary-search-tree/ +use super::util::tree::{TreeNode, to_tree}; + +pub fn insert_into_bst(root: Option>>, val: i32) -> Option>> { + insert(&root, val); + root +} +fn insert(node: &Option>>, val: i32) { + if let Some(n) = node { + let mut n = n.borrow_mut(); + let target = if val > n.val { &mut n.right } else { &mut n.left }; + + if target.is_some() { + return insert(target, val); + } + + *target = Some(Rc::new(RefCell::new(TreeNode::new(val)))); + } +} diff --git a/rust/24_binary_tree/max_depth_in_binary_tree.rs b/rust/24_binary_tree/max_depth_in_binary_tree.rs new file mode 100644 index 00000000..da36605b --- /dev/null +++ b/rust/24_binary_tree/max_depth_in_binary_tree.rs @@ -0,0 +1,38 @@ +// https://leetcode.com/problems/maximum-depth-of-binary-tree/ +use super::util::tree::{TreeNode, to_tree}; + +// max depth BFS +pub fn max_depth(root: Option>>) -> i32 { + if root.is_none() { return 0; } + + let mut depth = 0; + let mut deque: VecDeque>>> = VecDeque::new(); + deque.push_back(root); + + while !deque.is_empty() { + let level_size = deque.len(); + let mut added = false; + depth += 1; + for _i in 0..level_size { + added = true; + if let Some(Some(node)) = deque.pop_front() { + if node.borrow().left.is_some() { deque.push_back(node.borrow().left.clone());} + if node.borrow().right.is_some() { deque.push_back(node.borrow().right.clone());} + } + } + if !added { break; } + } + depth +} + +// max depth DFS +pub fn max_depth(root: Option>>) -> i32 { + match root { + Some(node) => { + let left = Self::max_depth(node.borrow().left.clone()); + let right = Self::max_depth(node.borrow().right.clone()); + 1 + left.max(right) + }, + _ => 0, + } +} diff --git a/rust/24_binary_tree/search_in_binary_tree.rs b/rust/24_binary_tree/search_in_binary_tree.rs new file mode 100644 index 00000000..7650c171 --- /dev/null +++ b/rust/24_binary_tree/search_in_binary_tree.rs @@ -0,0 +1,25 @@ +// https://leetcode.com/problems/search-in-a-binary-search-tree/ +use super::util::tree::{TreeNode, to_tree}; + +// Iterating method +pub fn search_bst(root: Option>>, val: i32) -> Option>> { + let mut r = root.clone(); + while let Some(node) = r { + if node.borrow().val == val { return Some(node); } + if node.borrow().val > val { + r = node.borrow().left.clone(); + } else { + r = node.borrow().right.clone(); + } + } + None +} + +// Recursive Approach +pub fn search_bst(root: Option>>, val: i32) -> Option>> { + if let Some(n) = &root { + if n.borrow().val > val { return Self::search_bst(n.borrow().left.clone(), val); } + if n.borrow().val < val { return Self::search_bst(n.borrow().right.clone(), val); } + } + root +} diff --git a/rust/24_binary_tree/util/tree.rs b/rust/24_binary_tree/util/tree.rs new file mode 100644 index 00000000..fc8c727e --- /dev/null +++ b/rust/24_binary_tree/util/tree.rs @@ -0,0 +1,57 @@ +use std::rc::Rc; +use std::cell::RefCell; + +#[derive(Debug, PartialEq, Eq)] +pub struct TreeNode { + pub val: i32, + pub left: Option>>, + pub right: Option>>, +} + +impl TreeNode { + #[inline] + pub fn new(val: i32) -> Self { + TreeNode { + val, + left: None, + right: None + } + } +} + +pub fn to_tree(vec: Vec>) -> Option>> { + use std::collections::VecDeque; + let head = Some(Rc::new(RefCell::new(TreeNode::new(vec[0].unwrap())))); + let mut queue = VecDeque::new(); + queue.push_back(head.as_ref().unwrap().clone()); + + for children in vec[1..].chunks(2) { + let parent = queue.pop_front().unwrap(); + if let Some(v) = children[0] { + parent.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(v)))); + queue.push_back(parent.borrow().left.as_ref().unwrap().clone()); + } + if children.len() > 1 { + if let Some(v) = children[1] { + parent.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(v)))); + queue.push_back(parent.borrow().right.as_ref().unwrap().clone()); + } + } + } + head +} + +#[macro_export] +macro_rules! tree { + () => { + None + }; + ($($e:expr),*) => { + { + let vec = vec![$(stringify!($e)), *]; + let vec = vec.into_iter().map(|v| v.parse::().ok()).collect::>(); + to_tree(vec) + } + }; + ($($e:expr,)*) => {(tree![$($e),*])}; +} diff --git a/rust/28_heap/build_heap.rs b/rust/28_heap/build_heap.rs new file mode 100644 index 00000000..d3f84dfc --- /dev/null +++ b/rust/28_heap/build_heap.rs @@ -0,0 +1,53 @@ +// 建堆,自底向上堆化 +pub fn build_heap_down_up(nums: &mut Vec) { + for i in 1..nums.len() { + heapify_down_up(nums, i); + } +} + +fn heapify_down_up(nums: &mut Vec, idx: usize) { + let mut idx = idx; + let mut parent_idx = idx - 1 >> 1; + while nums[idx] > nums[parent_idx] { + swap(nums, idx, parent_idx); + idx = parent_idx; + if idx == 0 { break; } + parent_idx = idx - 1 >> 1; + } +} + +// 建堆,自上向下堆化 +pub fn build_heap_up_down(nums: &mut Vec) { + let nums_len = nums.len(); + for i in (0..nums_len).rev() { + heapify_up_down(nums, i, nums_len); + } +} + +fn heapify_up_down(nums: &mut Vec, idx: usize, nums_len: usize) { + let mut idx = idx; + loop { + let mut max_pos = idx; + if 2 * idx + 1 < nums_len && nums[idx] < nums[2 * idx + 1] { max_pos = 2 * idx + 1; } + if 2 * idx + 2 < nums_len && nums[max_pos] < nums[2 * idx + 2] { max_pos = 2 * idx + 2; } + + if max_pos == idx { break; } + swap(nums, idx, max_pos); + idx = max_pos; + } +} + +fn swap(nums: &mut Vec, idx: usize, parent_idx: usize) { + let tmp = nums[parent_idx]; + nums[parent_idx] = nums[idx]; + nums[idx] = tmp; +} + +fn main() { + let mut nums = vec![1, 4, 5, 7, 8, 13, 16, 19, 20]; + build_heap_down_up(&mut nums); + println!("{:?}", nums); + let mut nums1 = vec![1, 4, 5, 7, 8, 13, 16, 19, 20]; + build_heap_up_down(&mut nums1); + println!("{:?}", nums1); +} diff --git a/rust/28_heap/heap.rs b/rust/28_heap/heap.rs new file mode 100644 index 00000000..c04f94cc --- /dev/null +++ b/rust/28_heap/heap.rs @@ -0,0 +1,98 @@ +#[derive(Debug)] +struct Heap { + data: Vec>, + capacity: usize, + count: i32, +} + +impl Heap { + pub fn new(capacity: usize) -> Self { + Heap { + data: vec![None; capacity], + capacity: capacity, + count: 0 + } + } + + pub fn insert(&mut self, x: i32) -> bool { + // 堆已满 + if self.capacity as i32 == self.count { return false; } + + self.data[self.count as usize] = Some(x); + if self.count == 0 { + self.count += 1; + return true; + } + + let mut idx = self.count as usize; + // 子节点大于父节点,子节点与父节点交换 + // 自底向上堆化 + let mut parent_idx = ((idx - 1) >> 1) as usize; + while parent_idx > 0 && self.data[idx] > self.data[parent_idx] { + self.swap(idx, parent_idx); + idx = parent_idx; + parent_idx = ((idx - 1) >> 1) as usize; + } + self.count += 1; + true + } + + pub fn remove_max(&mut self) -> Option { + // 堆已空 + if self.count == 0 { return None; } + + let max_value = self.data[0]; + // 将最后一个叶子节点移至堆顶 + self.data[0] = self.data[(self.count - 1) as usize]; + self.data[(self.count - 1) as usize] = None; + + self.heapify(); + self.count -= 1; + max_value + } + + // 堆化,自上向下堆化 + fn heapify(&mut self) { + let mut idx = 0usize; + loop { + let mut max_pos = idx; + if (2 * idx + 1) as i32 <= self.count && self.data[idx] < self.data[2 * idx + 1] { max_pos = 2 * idx + 1; } + if (2 * idx + 2) as i32 <= self.count && self.data[max_pos] < self.data[2 * idx + 2] { max_pos = 2 * idx + 2; } + + if max_pos == idx { break; } + self.swap(idx, max_pos); + idx = max_pos; + } + } + + fn swap(&mut self, idx: usize, parent_idx: usize) { + let tmp = self.data[parent_idx]; + self.data[parent_idx] = self.data[idx]; + self.data[idx] = tmp; + } +} + +fn main() { + let mut h = Heap::new(16); + h.insert(33); + h.insert(27); + h.insert(21); + h.insert(16); + h.insert(13); + h.insert(15); + h.insert(9); + h.insert(5); + h.insert(6); + h.insert(7); + h.insert(8); + h.insert(1); + h.insert(2); + h.insert(22); + println!("{:?}", h); + h.remove_max(); + println!("{:?}", h); + h.remove_max(); + println!("{:?}", h); + h.remove_max(); + println!("{:?}", h); +} diff --git a/rust/28_heap/sort_heap.rs b/rust/28_heap/sort_heap.rs new file mode 100644 index 00000000..d327cc2f --- /dev/null +++ b/rust/28_heap/sort_heap.rs @@ -0,0 +1,39 @@ +pub fn sort(nums: &mut Vec) { + build_heap(nums); + for i in (0..nums.len()).rev() { + swap(nums, 0, i); + heapify(nums, 0, i); + } +} + +fn build_heap(nums: &mut Vec) { + let nums_len = nums.len(); + for i in (0..nums_len).rev() { + heapify(nums, i, nums_len); + } +} + +fn heapify(nums: &mut Vec, idx: usize, nums_len: usize) { + let mut idx = idx; + loop { + let mut max_pos = idx; + if 2 * idx + 1 < nums_len && nums[idx] < nums[2 * idx + 1] { max_pos = 2 * idx + 1; } + if 2 * idx + 2 < nums_len && nums[max_pos] < nums[2 * idx + 2] { max_pos = 2 * idx + 2; } + + if max_pos == idx { break; } + swap(nums, idx, max_pos); + idx = max_pos; + } +} + +fn swap(nums: &mut Vec, idx: usize, parent_idx: usize) { + let tmp = nums[parent_idx]; + nums[parent_idx] = nums[idx]; + nums[idx] = tmp; +} + +fn main() { + let mut nums = vec![9, 6, 3, 1, 5]; + sort(&mut nums); + println!("{:?}", nums); +} diff --git a/rust/29_heap/get_median.rs b/rust/29_heap/get_median.rs new file mode 100644 index 00000000..f2db2eb0 --- /dev/null +++ b/rust/29_heap/get_median.rs @@ -0,0 +1,56 @@ +use std::collections::BinaryHeap; + +// 动态数组取位数 +// 对数组进行从小到大排序,数组下标为 n/2 的数据即为中位数 +fn get_median(nums: &mut Vec, x: i32) -> i32 { + let nums_len = nums.len(); + let mid = nums_len >> 1; + let mut max_heap = BinaryHeap::new(); + let mut min_heap = BinaryHeap::new(); + nums.sort(); + + // 将数组前半部分数据放入大顶堆 + // 数组后半部分数据入入小顶堆 + for i in 0..nums_len { + if i < mid { + max_heap.push(nums[i]); + } else { + min_heap.push(-nums[i]); + } + } + + nums.push(x); + + // 校验待插入数据 + // 若此数据小于大顶堆中顶数据,则将此数据插入大顶堆 + // 若此数据大于大顶堆中顶数据,将此数据插入小顶堆 + if x <= *max_heap.peek().unwrap() { + max_heap.push(x); + } else { + min_heap.push(-x); + } + + // 平衡两个堆 + // 大顶堆的数据个数一定小于等于小顶堆数据个数 + // 小顶堆数据个数一定是等于或者比大顶堆数据个数多1个 + // 不满足上述两个条件,即进行堆平衡 + if max_heap.len() > min_heap.len() { + min_heap.push(-max_heap.pop().unwrap()); + } else if min_heap.len() - max_heap.len() >= 2 { + max_heap.push(-min_heap.pop().unwrap()); + } + + -*min_heap.peek().unwrap() +} + +fn main() { + let mut nums = vec![12, 45, 30, 77, 5, 6, 7, 8]; + let m = get_median(&mut nums, 9); + println!("{:?}", m); // 9 + let n = get_median(&mut nums, 20); + println!("{:?}", n); // 12 + let h = get_median(&mut nums, 11); + println!("{:?}", h); // 11 + let i = get_median(&mut nums, 10); + println!("{:?}", i); // 11 +} diff --git a/rust/29_heap/get_top_k.rs b/rust/29_heap/get_top_k.rs new file mode 100644 index 00000000..f96e99b0 --- /dev/null +++ b/rust/29_heap/get_top_k.rs @@ -0,0 +1,42 @@ +use std::collections::BinaryHeap; + +// 动态数组取 top k 元素 +fn get_top_k(nums: &mut Vec, k: i32, x: i32) -> Vec { + let nums_len = nums.len() as i32; + if nums_len <= k { return nums.clone(); } + + let mut heap = BinaryHeap::new(); + + // 先将数组的前k个数据放入堆中 + for _i in 0..k { + heap.push(-nums[k as usize]); + } + + // 对数组中其它数据进行迭代,若数据大于堆顶元素,将堆顶元素移除,将此数据放入堆中 + for i in k + 1..nums_len { + if -nums[i as usize] < *heap.peek().unwrap() { + heap.pop(); + heap.push(-nums[i as usize]); + } + } + + // 向数组中插入新数据 + nums.push(x); + + // 新插入的数据若大于堆顶元素,将堆顶元素移除,将此数据放入堆中 + if -x < *heap.peek().unwrap() { + heap.pop(); + heap.push(-x); + } + + // let m: Vec = heap.iter().map(|h| h * -1).collect(); + // m + + heap.iter().map(|h| h * -1).collect::>() +} + +fn main() { + let mut nums = vec![4, 5, 7, 9, 10, 6, 11]; + let m = get_top_k(&mut nums, 3, 23); + println!("{:?}", m); +} diff --git a/rust/29_heap/merge_sorted_array.rs b/rust/29_heap/merge_sorted_array.rs new file mode 100644 index 00000000..fa220dad --- /dev/null +++ b/rust/29_heap/merge_sorted_array.rs @@ -0,0 +1,38 @@ +use std::collections::BinaryHeap; + +fn merge_sorted_array(nums1: &mut Vec, nums2: &mut Vec, nums3: &mut Vec) -> Vec { + let mut new_nums = vec![]; + let mut heap = BinaryHeap::new(); + + // Rust heap 是大顶堆,将待入堆的数值取反后再入堆,堆顶即为最小值,即达到小顶堆效果 + heap.push(-nums1[0]); + heap.push(-nums2[0]); + heap.push(-nums3[0]); + + while !nums1.is_empty() || !nums2.is_empty() || !nums3.is_empty() { + if heap.is_empty() { break; } + let num = -heap.pop().unwrap(); + new_nums.push(num); + + if !nums1.is_empty() && num == nums1[0] { + nums1.remove(0); + if !nums1.is_empty() { heap.push(-nums1[0]); } + } else if !nums2.is_empty() && num == nums2[0] { + nums2.remove(0); + if !nums2.is_empty() { heap.push(-nums2[0]); } + } else if !nums3.is_empty() && num == nums2[0] { + nums3.remove(0); + if !nums3.is_empty() { heap.push(-nums3[0]); } + } + } + new_nums +} + +fn main() { + let mut nums1 = vec![4, 5, 20, 90, 95, 100]; + let mut nums2 = vec![1, 6, 7, 8, 11, 23, 67, 89]; + let mut nums3 = vec![2, 5, 9, 30, 45]; + let new_nums = merge_sorted_array(&mut nums1, &mut nums2, &mut nums3); + + println!("{:?}", new_nums); +} diff --git a/rust/31_graph/graph_search.rs b/rust/31_graph/graph_search.rs new file mode 100644 index 00000000..ad05d4c3 --- /dev/null +++ b/rust/31_graph/graph_search.rs @@ -0,0 +1,112 @@ +use std::collections::LinkedList; +use std::collections::VecDeque; + +// 无向图 +#[derive(Debug)] +struct Graph { + v: i32, + linked_vec: Vec>, // 邻接表 +} + +impl Graph { + fn new(v: i32) -> Self { + Graph { + v: v, + linked_vec: vec![LinkedList::new(); v as usize], + } + } + + // 无向图的每条边存两次 + fn add_edge(&mut self, s: i32, t: i32) { + self.linked_vec[s as usize].push_back(t); + self.linked_vec[t as usize].push_back(s); + } + + fn bfs(&self, s: i32, t: i32) { + if s == t { return; } + + let mut prev = vec![-1; self.v as usize]; + let mut visited = vec![false; self.v as usize]; + let mut queue = VecDeque::new(); + + visited[s as usize] = true; + queue.push_back(s); + + while !queue.is_empty() { + let w = queue.pop_front().unwrap(); + for item in self.linked_vec[w as usize].iter() { + if visited[*item as usize] { continue; } + prev[*item as usize] = w; + if *item == t { + self.draw(&prev, s, t); + return; + } + visited[*item as usize] = true; + queue.push_back(*item); + } + } + } + + fn dfs(&self, s: i32, t: i32) { + let mut found = false; + let mut prev = vec![-1; self.v as usize]; + let mut visited = vec![false; self.v as usize]; + + self.recur_dfs(s, t, &mut visited, &mut prev, &mut found); + self.draw(&prev, s, t); + } + + fn recur_dfs(&self, + s: i32, + t: i32, + visited: &mut Vec, + prev: &mut Vec, + found: &mut bool) { + if *found == true { return; } + visited[s as usize] = true; + if s == t { + *found = true; + return; + } + for item in self.linked_vec[s as usize].iter() { + if visited[*item as usize] { continue; } + prev[*item as usize] = s; + self.recur_dfs(*item, t, visited, prev, found); + } + } + + // 递归打印路径 + fn draw(&self, prev: &Vec, s: i32, t: i32) { + if prev[t as usize] != -1 && s != t { + self.draw(prev, s, prev[t as usize]); + } + + println!("{} ->", t); + } +} + +fn main() { + let mut graph = Graph::new(8); + graph.add_edge(0, 1); + graph.add_edge(0, 3); + graph.add_edge(1, 2); + graph.add_edge(1, 4); + graph.add_edge(2, 5); + graph.add_edge(3, 4); + graph.add_edge(4, 5); + graph.add_edge(4, 6); + graph.add_edge(5, 7); + graph.add_edge(6, 7); + + // Graph { v: 8, linked_vec: [[1, 3], [0, 2, 4], [1, 5], [0, 4], [1, 3, 5, 6], [2, 4, 7], [4, 7], [5, 6]] } + println!("{:?}", graph); + graph.bfs(0, 7); + println!("bfs============="); + graph.bfs(1, 3); + println!("bfs============="); + + graph.dfs(0, 7); + println!("dfs============="); + graph.dfs(1, 3); + println!("dfs============="); +} diff --git a/rust/32_string/bf_rk.rs b/rust/32_string/bf_rk.rs new file mode 100644 index 00000000..a04f3f7c --- /dev/null +++ b/rust/32_string/bf_rk.rs @@ -0,0 +1,73 @@ +use std::collections::HashMap; + +fn bf(primary: &str, pattern: &str) -> i32 { + if primary.is_empty() || pattern.is_empty() || primary.len() < pattern.len() { return -1; } + + let primary_chars: Vec = primary.chars().collect(); + let pattern_chars: Vec = pattern.chars().collect(); + for i in 0..(primary.len() - pattern.len() + 1) { + if pattern_chars == primary_chars[i..i + pattern.len()].to_vec() { + return i as i32; + } + } + -1 +} + +// 通过哈希算法对主串中的 n-m+1 个子串分别求哈希值, +// 逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相等,那就说明对应的子串和模式串匹配 +fn rk(primary: &str, pattern: &str) -> i32 { + if primary.is_empty() || pattern.is_empty() || primary.len() < pattern.len() { return -1; } + + let primary_chars: Vec = primary.chars().collect(); + let pattern_chars: Vec = pattern.chars().collect(); + let base: i128 = 26; + let m = pattern.len(); + let n = primary.len(); + let mut pow_vec = vec![]; + let mut hash = HashMap::new(); + + // 存储 26 的 n 次方到数组中,方便后面调用 + for i in 0..m { + pow_vec.push(base.pow(i as u32)); + } + + // 计算子串的 hash 值 + let mut p_value = 0; + for i in 0..m { + p_value += (pattern_chars[i] as i128 - 'a' as i128) * pow_vec[m-1-i]; + } + + // 计算主串的 n-m+1 个子串的 hash 值 + for i in 0..(n - m + 1) { + // 计算主串中 index 为 0 的子串的 hash 值 + let mut value = 0; + if i == 0 { + for i in 0..m { + value += (primary_chars[i] as i128 - 'a' as i128) * pow_vec[m-1-i]; + } + } else { + // 计算 index 为 i 的子串的 hash 值 + // 计算公式: hash[i] = (hash[i-1] - 26^(m-1) * (primary_chars[i-1] - 'a')) * 26 + (26^0 * (primary_chars[i+m-1] - 'a')) + value = (hash[&((i-1) as i32)] - base.pow((m-1) as u32) * (primary_chars[i-1] as i128 - 'a' as i128)) * base + ((primary_chars[i+m-1]) as i128 - 'a' as i128); + } + + // hash 值相等,比较两个串内容是否相等,避免 hash 碰撞 + if value == p_value && pattern_chars == primary_chars[i..i+m].to_vec() { + return i as i32; + } + + hash.insert(i as i32, value); + } + + -1 +} + +fn main() { + let primary = "thequickbrownfoxjumpsoverthelazydog"; + let pattern = "jump"; + let result = bf(primary, pattern); + println!("{}", result); // 16 + + let result2 = rk(primary, pattern); + println!("{:?}", result2); // 16 +} diff --git a/rust/33_string/bm.rs b/rust/33_string/bm.rs new file mode 100644 index 00000000..f6f891db --- /dev/null +++ b/rust/33_string/bm.rs @@ -0,0 +1,80 @@ +// 生成模式串散列表 +fn generate_bc(pattern: &str) -> Vec { + let mut bc: Vec = vec![-1; 256]; + let pattern_chars: Vec = pattern.chars().collect(); + for (index, item) in pattern_chars.iter().enumerate() { + bc[(*item as u8) as usize] = index as i32; + } + bc +} + +// 计算 suffix 数组与 prefix 数组 +fn generate_gs(pattern: &str) -> (Vec, Vec) { + let m = pattern.len(); + let mut suffix: Vec = vec![-1; m]; + let mut prefix: Vec = vec![false; m]; + let pattern_chars: Vec = pattern.chars().collect(); + + for i in 0..m-1 { + let mut j = i as i32; + let mut k = 0; + + while j >= 0 && pattern_chars[j as usize] == pattern_chars[m-k-1] { + j -= 1; + k += 1; + suffix[k] = j + 1; + } + + if j == -1 { prefix[k] = true; } + } + + (suffix, prefix) +} + +fn move_by_gs(bad_char_start_index: usize, pattern_len: usize, suffix: &Vec, prefix: &Vec) -> i32 { + // 好后缀长度 + let k = pattern_len - bad_char_start_index - 1; + // 完全匹配 + if suffix[k] != -1 { return (bad_char_start_index + 1 - suffix[k] as usize) as i32; } + + for i in pattern_len+2..bad_char_start_index { + if prefix[pattern_len-i] { return i as i32; } + } + // 没有匹配 + pattern_len as i32 +} + +fn bm_search(primary: &str, pattern: &str) -> i32 { + if primary.is_empty() || pattern.is_empty() || pattern.len() > primary.len() { return 0; } + + let primary_chars: Vec = primary.chars().collect(); + let pattern_chars: Vec = pattern.chars().collect(); + let bc = generate_bc(pattern); + let (suffix, prefix) = generate_gs(pattern); + let n = primary.len(); + let m = pattern.len(); + let mut i = 0; + + while i <= n - m { + let mut j = (m-1) as i32; + while j >=0 { + if primary_chars[i+j as usize] != pattern_chars[j as usize] { break; } + j -= 1 + } + if j < 0 { return i as i32; } + let step_for_bc = j as i32 - bc[(primary_chars[i+j as usize] as u8) as usize]; + let mut step_for_gs = 0; + if j < (m-1) as i32 { + step_for_gs = move_by_gs(j as usize, m, &suffix, &prefix); + } + i = (i as i32 + step_for_bc.max(step_for_gs)) as usize; + } + -1 +} + +fn main() { + let primary = "abcacabcbcabcabc"; + let pattern = "cabcab"; + let m = bm_search(primary, pattern); + println!("{:?}", m); +} diff --git a/rust/34_string/kmp.rs b/rust/34_string/kmp.rs new file mode 100644 index 00000000..6731994d --- /dev/null +++ b/rust/34_string/kmp.rs @@ -0,0 +1,58 @@ +fn kmp_search(primary: &str, pattern: &str) -> Vec { + if primary.is_empty() || pattern.is_empty() || pattern.len() > primary.len() { return vec![0]; } + + let primary_chars: Vec = primary.chars().collect(); + let pattern_chars: Vec = pattern.chars().collect(); + let max_match_lengths = get_failure_function(pattern); + let mut count = 0; + let m = pattern.len(); + let mut positions = vec![]; + + for i in 0..primary.len() { + while count > 0 && pattern_chars[count as usize] != primary_chars[i] { + count = max_match_lengths[(count-1) as usize]; + } + + if pattern_chars[count as usize] == primary_chars[i] { + count += 1; + } + + if count as usize == m { + positions.push((i - m + 1) as i32); + count = max_match_lengths[(count-1) as usize]; + } + } + + positions +} + +fn get_failure_function(pattern: &str) -> Vec { + let m = pattern.len(); + let mut max_match_lengths: Vec = vec![0; m]; + let mut max_length: i32 = 0; + let pattern_chars: Vec = pattern.chars().collect(); + + for i in 1..m { + while max_length > 0 && pattern_chars[max_length as usize] != pattern_chars[i] { + max_length = max_match_lengths[(max_length-1) as usize]; + } + + if pattern_chars[i] == pattern_chars[max_length as usize] { + max_length += 1; + } + + max_match_lengths[i] = max_length; + } + + max_match_lengths +} + +fn main() { + let primary1 = "abbaabbaaba"; + let pattern1 = "abbaaba"; + println!("{:?}", kmp_search(primary1, pattern1)); // 4 + + let primary = "abc abcdab abcdabcdabde"; + let pattern = "bcdabd"; + println!("{:?}", kmp_search(primary, pattern)); // 16 +} diff --git a/rust/35_trie/trie.rs b/rust/35_trie/trie.rs new file mode 100644 index 00000000..abfb2a5d --- /dev/null +++ b/rust/35_trie/trie.rs @@ -0,0 +1,41 @@ +// [leetcode 208](https://leetcode.com/problems/implement-trie-prefix-tree/) + +#[derive(Default, Debug)] +struct Trie { + is_ending: bool, + nodes: [Option>; 26], +} + +impl Trie { + fn new() -> Self { + Default::default() + } + + fn insert(&mut self, word: &str) { + let mut curr = self; + for i in word.chars().map(|c| (c as usize - 'a' as usize) as usize) { + curr = curr.nodes[i].get_or_insert_with(|| Box::new(Trie::new())); + } + curr.is_ending = true; + } + + fn find(&self, word: &str) -> bool { + let mut curr = self; + for i in word.chars().map(|c| (c as usize - 'a' as usize) as usize) { + match curr.nodes[i].as_ref() { + Some(node) => { curr = node; }, + None => { return false; }, + } + } + curr.is_ending + } +} + +fn main() { + let mut m = Trie::new(); + m.insert("hello"); + m.insert("she"); + println!("{:?}", m); + let r = m.search("hello"); + println!("{}", r); // true +} diff --git a/rust/38_divide_and_conquer/merge_sort_count.rs b/rust/38_divide_and_conquer/merge_sort_count.rs new file mode 100644 index 00000000..54aea9d9 --- /dev/null +++ b/rust/38_divide_and_conquer/merge_sort_count.rs @@ -0,0 +1,57 @@ +fn merge_sort_count(mut nums: Vec) -> i32 { + let mut count = 0; + let n = nums.len() - 1; + merge_sort(&mut nums, 0, n, &mut count); + count +} + +fn merge_sort(nums: &mut Vec, low: usize, high: usize, count: &mut i32) { + if low >= high { return; } + + let middle = low + ((high - low) >> 1); + merge_sort(nums, low, middle, count); + merge_sort(nums, middle+1, high, count); + + merge(nums, low, middle, high, count); +} + +fn merge(nums: &mut Vec, + low: usize, + middle: usize, + high: usize, + count: &mut i32) { + let mut i = low; + let mut j = middle + 1; + let mut tmp = vec![]; + + while i <= middle && j <= high { + if nums[i] <= nums[j] { + tmp.push(nums[i]); + i += 1; + } else { + // count += &(middle - i + 1); + *count += (middle - i + 1) as i32; + tmp.push(nums[j]); + j += 1; + } + } + + while i <= middle { + tmp.push(nums[i]); + i += 1; + } + + while j <= high { + tmp.push(nums[j]); + j += 1; + } + + for i in 0..=(high-low) { + nums[low+1] = tmp[i]; + } +} +fn main() { + let nums = vec![1, 5, 6, 2, 3, 4]; + let m = merge_sort_count(nums); + println!("{:?}", m); +} diff --git a/rust/39_back_tracking/bag.rs b/rust/39_back_tracking/bag.rs new file mode 100644 index 00000000..9b7f54e2 --- /dev/null +++ b/rust/39_back_tracking/bag.rs @@ -0,0 +1,74 @@ +use std::collections::HashMap; + +fn solve_bag(items: Vec, capacity: i32) -> HashMap> { + let pick_idx = 0; + let current_weight = 0; + let mut picks = vec![-1; items.len()]; + let mut max_values = vec![-1; items.len()]; + let mut result = HashMap::new(); + + bag(pick_idx, + current_weight, + &items, + capacity, + &mut picks, + &mut max_values, + &mut result,); + + result +} + +fn bag(pick_idx: i32, + current_weight: i32, + items: &Vec, + capacity: i32, + picks: &mut Vec, + max_values: &mut Vec, + result: &mut HashMap>) { + if current_weight == capacity || pick_idx == items.len() as i32 { + if get_value(items, &picks) > get_value(items, max_values) { + for i in 0..picks.len() { + max_values[i] = picks[i]; + } + result.insert(get_value(items, max_values), picks.to_vec()); + } + return; + } + + // 选 + if current_weight + items[pick_idx as usize] <= capacity { + picks[pick_idx as usize] = 1; + bag(pick_idx + 1, + current_weight + items[pick_idx as usize], + items, + capacity, + picks, + max_values, + result); + } + + // 不选 + picks[pick_idx as usize] = 0; + bag(pick_idx + 1, + current_weight, + items, + capacity, + picks, + max_values, + result); + +} + +fn get_value(items: &Vec, picks: &Vec) -> i32 { + let mut result = 0; + for i in 0..picks.len() { + if picks[i] == 1 { result += items[i]; } + } + result +} + +fn main() { + let items = vec![2, 2, 4, 6, 3]; + let m = solve_bag(items, 10); + println!("{:?}", m); // {10: [1, 1, 0, 1, 0], 8: [1, 1, 1, 0, 0]} +} diff --git a/rust/39_back_tracking/bag_exec.rs b/rust/39_back_tracking/bag_exec.rs new file mode 100644 index 00000000..a94a5741 --- /dev/null +++ b/rust/39_back_tracking/bag_exec.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; + +fn solve_bag(items: Vec<(i32, i32)>, capacity: i32) -> HashMap> { + let pick_idx = 0; + let current_weight = 0; + let mut picks = vec![-1; items.len()]; + let mut max_values = vec![-1; items.len()]; + let mut result = HashMap::new(); + + bag(pick_idx, + current_weight, + &items, + capacity, + &mut picks, + &mut max_values, + &mut result,); + + result +} + +fn bag(pick_idx: i32, + current_weight: i32, + items: &Vec<(i32, i32)>, + capacity: i32, + picks: &mut Vec, + max_values: &mut Vec, + result: &mut HashMap>) { + if current_weight == capacity || pick_idx == items.len() as i32 { + if get_value(items, &picks) > get_value(items, max_values) { + for i in 0..picks.len() { + max_values[i] = picks[i]; + } + result.insert(get_value(items, max_values), picks.to_vec()); + } + return; + } + + // 选 + let item_weight = items[pick_idx as usize].0; + if current_weight + item_weight <= capacity { + picks[pick_idx as usize] = 1; + bag(pick_idx + 1, + current_weight + item_weight, + items, + capacity, + picks, + max_values, + result); + } + + // 不选 + picks[pick_idx as usize] = 0; + bag(pick_idx + 1, + current_weight, + items, + capacity, + picks, + max_values, + result); + +} + +fn get_value(items: &Vec<(i32, i32)>, picks: &Vec) -> i32 { + let mut result = 0; + for i in 0..picks.len() { + if picks[i] == 1 { result += items[i].1; } + } + result +} + +fn main() { + // [(weight, value)...] + let items = vec![(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)]; + let m = solve_bag(items, 10); + println!("{:?}", m); // {13: [1, 1, 1, 1, 0], 21: [1, 1, 1, 0, 1]} +} diff --git a/rust/39_back_tracking/n_queens.rs b/rust/39_back_tracking/n_queens.rs new file mode 100644 index 00000000..fc0fdf2c --- /dev/null +++ b/rust/39_back_tracking/n_queens.rs @@ -0,0 +1,45 @@ +# leetcode 51 [N-queens])https://leetcode.com/problems/n-queens/) + +pub fn solve_n_queens(n: i32) -> Vec> { + let mut board = vec![vec!['.'; n as usize]; n as usize]; + let mut solution = vec![]; + schedule_queens(&mut board, &mut solution, n as usize, 0); + solution +} + +fn schedule_queens(board: &mut Vec>, solution: &mut Vec>, len: usize, row: usize) { + for col in 0..len { + if !collision(&board, len, row, col) { + board[row][col] = 'Q'; + if row == len - 1 { + solution.push(board.iter().map(|vec| vec.iter().collect()).collect()); + } else { + schedule_queens(board, solution, len, row+1); + } + board[row][col] = '.'; + } + } +} + +#[inline(always)] +fn collision(board: &Vec>, len: usize, row: usize, col: usize) -> bool { + for i in 0..row { + if board[i][col] == 'Q' { return true } + } + let (mut i, mut j) = (row as i32 - 1, col as i32 - 1); + while i >= 0 && j >= 0 { + if board[i as usize][j as usize] == 'Q' { return true } + i -= 1; j -= 1; + } + let (mut i, mut j) = (row as i32 - 1, col as i32 + 1); + while i >= 0 && j < len as i32 { + if board[i as usize][j as usize] == 'Q' { return true} + i -= 1; j += 1; + } + false +} + +fn main() { + let m = solve_n_queens(8); + println!("{:?}", m); +} diff --git a/rust/39_back_tracking/regex.rs b/rust/39_back_tracking/regex.rs new file mode 100644 index 00000000..60abc4a3 --- /dev/null +++ b/rust/39_back_tracking/regex.rs @@ -0,0 +1,49 @@ +fn regex_match(text: &str, regex: &str) -> bool { + let mut matched = false; + let text_chars = text.chars().collect(); + let regex_chars = regex.chars().collect(); + rematch(&text_chars, ®ex_chars, 0, 0, &mut matched); + matched +} + +fn rematch(text_chars: &Vec, + regex_chars: &Vec, + t_idx: usize, + r_idx: usize, + matched: &mut bool) { + // 已经匹配好了,直接返回; + if *matched { return; } + + // 正则串已经全部匹配 + if r_idx >= regex_chars.len() { + *matched = true; + return; + } + + // 主串已经匹配完,但是正则串还没有全部进行匹配 + if t_idx >= text_chars.len() && r_idx < regex_chars.len() { + *matched = false; + return; + } + + // * 匹配1个或多个字符,递归进行匹配 + if regex_chars[r_idx] == '*' { + for i in t_idx..text_chars.len() { + rematch(text_chars, regex_chars, i+1, t_idx+1, matched); + } + // ? 匹配0个或1个字符,分两种情况进行匹配 + } else if regex_chars[r_idx] == '?' { + rematch(text_chars, regex_chars, t_idx+1, r_idx+1, matched); + rematch(text_chars, regex_chars, t_idx, r_idx+1, matched); + // 非特殊字符,精确匹配 + } else if regex_chars[r_idx] == text_chars[t_idx] { + rematch(text_chars, regex_chars, t_idx+1, r_idx+1, matched); + } +} + +fn main() { + let text = "abcdsadfkjlekjoiwjiojieeecd"; + let regex = "ab*eee?d"; + let m = regex_match(text, regex); + println!("{}", m); +} diff --git a/rust/40_dynamic_programming/bag.rs b/rust/40_dynamic_programming/bag.rs new file mode 100644 index 00000000..cf62dc14 --- /dev/null +++ b/rust/40_dynamic_programming/bag.rs @@ -0,0 +1,42 @@ +fn knapsack(items: Vec, capacity: i32) -> i32 { + let mut states = vec![vec![false; (capacity + 1) as usize]; items.len()]; + let mut result = vec![]; + states[0][0] = true; + if items[0] <= capacity { states[0][items[0] as usize] = true; } + for i in 1..items.len() { + for j in 0..=capacity as usize { + if states[i-1][j] { states[i][j] = true; } + } + + for j in 0..=(capacity - items[i]) as usize { + if states[i-1][j] { states[i][j + items[i] as usize] = true; } + } + } + + let mut idx = capacity; + while idx <= capacity { + if states[items.len()-1][idx as usize] { break; } + idx += 1; + } + for i in (1..items.len()).rev() { + if idx - items[i] >= 0 && states[i-1][(idx-items[i]) as usize] { + idx -= items[i]; + result.push(items[i]); + } + } + + if idx != 0 { result.push(items[0]); } + println!("{:?}", result); + + for i in (0..=capacity as usize).rev() { + if states[items.len()-1][i] { return i as i32; } + } + 0 +} + +fn main() { + let items = vec![2, 2, 4, 6, 3]; + let capacity = 9; + let m = knapsack(items, capacity); + println!("{}", m); +} diff --git a/rust/40_dynamic_programming/knapsack.rs b/rust/40_dynamic_programming/knapsack.rs new file mode 100644 index 00000000..326c0342 --- /dev/null +++ b/rust/40_dynamic_programming/knapsack.rs @@ -0,0 +1,32 @@ +fn knapsack(items: Vec<(i32, i32)>, capacity: i32) -> i32 { + let mut states = vec![-1; (capacity + 1) as usize]; + let mut result = vec![]; + states[0] = 0; + if items[0].0 <= capacity { states[items[0].0 as usize] = items[0].1; } + for i in 1..items.len() { + for j in 0..=(capacity - items[i].0) as usize { + if states[j] >= 0 { + let value = states[j] + items[i].1; + if value > states[j+items[i].0 as usize] { + states[j+items[i].0 as usize] = value; + result.push(items[i].0); + } + } + } + } + + let mut max_value = -1; + for i in (0..=capacity as usize).rev() { + if states[i] >= max_value { + max_value = states[i]; + } + } + max_value +} + +fn main() { + let items = vec![(2, 3), (2, 4), (4, 8), (6, 9), (3, 6)]; + let capacity = 9; + let m = knapsack(items, capacity); + println!("{}", m); +} diff --git a/rust/40_dynamic_programming/triangle.rs b/rust/40_dynamic_programming/triangle.rs new file mode 100644 index 00000000..096ca6fb --- /dev/null +++ b/rust/40_dynamic_programming/triangle.rs @@ -0,0 +1,23 @@ +# leetcode [minimum_total](https://leetcode.com/problems/triangle/) + +pub fn minimum_total(mut triangle: Vec>) -> i32 { + if triangle.len() == 0 { return 0; } + + for i in (0..triangle.len() - 1).rev() { + for j in 0..triangle[i].len() { + triangle[i][j] = triangle[i+1][j].min(triangle[i+1][j+1]) + triangle[i][j]; + } + } + triangle[0][0] +} + +fn main() { + let triangle = vec![ + vec![2], + vec![3, 4], + vec![6, 5, 7], + vec![4, 1, 8, 3], + ]; + + println!("{:?}", minimum_total(triangle)); +} diff --git a/scala/.gitignore b/scala/.gitignore new file mode 100644 index 00000000..2f7896d1 --- /dev/null +++ b/scala/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/scala/build.sbt b/scala/build.sbt new file mode 100644 index 00000000..9972b7ff --- /dev/null +++ b/scala/build.sbt @@ -0,0 +1,7 @@ +lazy val root = (project in file(".")) + .settings( + name := "algo-scala", + version := "1.0", + scalaVersion := "2.12.8", + libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test" + ) diff --git a/scala/project/build.properties b/scala/project/build.properties new file mode 100644 index 00000000..5620cc50 --- /dev/null +++ b/scala/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.1 diff --git a/scala/src/main/scala/ch05_array/ArrayDemo.scala b/scala/src/main/scala/ch05_array/ArrayDemo.scala new file mode 100644 index 00000000..73b5e7d6 --- /dev/null +++ b/scala/src/main/scala/ch05_array/ArrayDemo.scala @@ -0,0 +1,53 @@ +package ch05_array + +class ArrayDemo(capacity: Int) { + + var data: Array[Char] = new Array[Char](capacity) + var length: Int = 0 + + def find(index: Int): Char = { + if (index < 0 || index > length) { + return 0.toChar + } + data(index) + } + + def insert(index: Int, value: Char): Boolean = { + if (length == capacity) { + return false + } + + if (index < 0 || index >= capacity) { + return false + } + + for (i <- length until index by -1) { + data(i) = data(i - 1) + } + data(index) = value + length += 1 + + true + } + + def delete(index: Int): Char = { + if (length == 0) { + throw new IllegalStateException("array is empty") + } + if (index >= length) { + throw new IllegalStateException("index out of range, current data length is " + length) + } + val result = data(index) + for (i <- index until length-1) { + data(i) = data(i + 1) + } + + length -= 1 + result + } + + def print: String = { + data.subSequence(0, length).toString + } + +} diff --git a/scala/src/main/scala/ch06_linkedlist/SinglyLinkedList.scala b/scala/src/main/scala/ch06_linkedlist/SinglyLinkedList.scala new file mode 100644 index 00000000..a57c9ba7 --- /dev/null +++ b/scala/src/main/scala/ch06_linkedlist/SinglyLinkedList.scala @@ -0,0 +1,222 @@ +package ch06_linkedlist + +import scala.util.control.Breaks._ + +// the model class for the linked list +class Node(var data: Int, var next: Option[Node]) + +class SinglyLinkedList(var headOpt: Option[Node]) { + + //define constructor without param + def this() = this(None) + + + def findByValue(value: Int): Option[Node] = { + headOpt.flatMap(head => { + var node = head + while (!node.data.equals(value) && node.next.isDefined) { + node = node.next.get + } + + if (node.data.equals(value)) { + return Some(node) + } else { + //arrive the end of the chain + return None + } + }) + } + + def insertToHead(value: Int): Unit = { + val newNode = new Node(value, None) + insertToHead(newNode) + } + + def insertToHead(newNode: Node): Unit = { + headOpt match { + case None => + //it's an empty linked list, make new node as head + headOpt = Some(newNode) + case Some(head) => + newNode.next = Some(head) + headOpt = Some(newNode) + } + } + + def insertTail(value: Int): Unit = { + val newNode = new Node(value, None) + insertTail(newNode) + } + + def insertTail(newNode: Node): Unit = { + headOpt match { + case None => + //it's an empty linked list, make new node as head + headOpt = Some(newNode) + case Some(head) => + //need to start to find from head to current tail + var node = head + while (node.next.isDefined) { + node = node.next.get + } + //now node is the tail as node.next is None + //add new tail + node.next = Some(newNode) + } + } + + def insertAfter(existNode: Node, value: Int): Unit = { + val newNode = new Node(value, None) + insertAfter(existNode, newNode) + } + + def insertAfter(existNode: Node, newNode: Node): Unit = { + existNode.next match { + case None => + //exist node is tail + newNode.next = None + existNode.next = Some(newNode) + case Some(next) => + newNode.next = Some(next) + existNode.next = Some(newNode) + } + } + + def insertBefore(existNode: Node, value: Int): Unit = { + val newNode = new Node(value, None) + insertBefore(existNode, newNode) + } + + def insertBefore(existNode: Node, newNode: Node): Unit = { + headOpt match { + case None => + throw new IllegalStateException("head node should not be None") + case Some(head) => + if (existNode.equals(head)) { + insertToHead(newNode) + } + var node = head + while (node.next.isDefined && !node.next.get.equals(existNode)) { + node = node.next.get + } + + if (node.next.isEmpty) { + throw new IllegalArgumentException("existNode " + existNode + " does not exist in this chain") + } + + newNode.next = node.next + node.next = Some(newNode) + } + } + + def deleteByNode(node: Node): Unit = { + headOpt.map(head => { + if (head.equals(node)) { + //deleting head + headOpt = node.next + } else { + var p: Node = head + while (p.next.isDefined && !p.next.get.equals(node)) { + p = p.next.get + } + + if (p.next.isEmpty) { + throw new IllegalArgumentException("could not find given node") + } + p.next = node.next + } + + }) + } + + //inverse the link from given node to head + def inverseLink(node: Node): Node = { + if (headOpt.isEmpty) { + throw new IllegalArgumentException("list is empty") + } + + var pre: Option[Node] = None + var next: Option[Node] = None + var current: Option[Node] = headOpt + + while (current.isDefined && !current.get.equals(node)) { + next = current.get.next + current.get.next = pre + + pre = current + current = next + } + + current.get.next = pre + + current.get + + } + + def isPalindrome(): Boolean = { + headOpt match { + case None => false + case Some(head) => + var p: Node = head + var q: Node = head + + if (p.next.isEmpty) { + //we only got 1 element in the chain + return true + } + + //start to find middle of the chain + while (q.next.isDefined && q.next.get.next.isDefined) { + p = p.next.get + q = q.next.get.next.get + } + var leftLink: Option[Node] = None + var rightLink: Option[Node] = None + q.next match { + case None => + //p is in the middle of an odd numbers of chain + rightLink = p.next + leftLink = inverseLink(p).next + case Some(_) => + //p and p.next is in the middle of the even numbers of chain + rightLink = p.next + leftLink = Some(inverseLink(p)) + } + + compareLinkedNodes(leftLink, rightLink) + } + } + + def compareLinkedNodes(leftLink: Option[Node], rightLink: Option[Node]): Boolean = { + var left = leftLink + var right = rightLink + + breakable { + while (left.isDefined && right.isDefined) { + if (!left.get.data.equals(right.get.data)) { + break + } + left = left.get.next + right = right.get.next + } + } + //make sure we have loop until the end of the chain + left.isEmpty && right.isEmpty + } + + def mkString(): String = { + headOpt.map(head => { + var node = head + val result = new StringBuilder + + while (node.next.isDefined) { + result.append(node.data) + node = node.next.get + } + result.append(node.data) + + result.mkString + }).getOrElse("") + } + +} diff --git a/scala/src/main/scala/ch07_linkedlist/LinkedListAlgo.scala b/scala/src/main/scala/ch07_linkedlist/LinkedListAlgo.scala new file mode 100644 index 00000000..00af2b79 --- /dev/null +++ b/scala/src/main/scala/ch07_linkedlist/LinkedListAlgo.scala @@ -0,0 +1,181 @@ +package ch07_linkedlist + +import ch06_linkedlist.Node + +object LinkedListAlgo { + + //reverse a linked list + def reverse(head: Node): Node = { + if (head.next.isEmpty) { + //single node of linked list + return head + } + var prev: Option[Node] = None + var current: Option[Node] = Some(head) + var next: Option[Node] = None + + while (current.get.next.isDefined) { + next = current.get.next + current.get.next = prev + + prev = current + current = next + } + //for the tail of the node, make it point to previous node to complete the reverse + current.get.next = prev + + current.get + } + + /** + * + * @param head + * @return Some(Node) a node in a circle or None + */ + def checkCircle(head: Node): Option[Node] = { + var fast = head + var slow = head + + while (fast.next.isDefined && fast.next.get.next.isDefined) { + fast = fast.next.get.next.get + slow = slow.next.get + + if (fast.equals(slow)) { + return Some(slow) + } + } + None + } + + /** + * calculate the length of the circle + * + * @param node - some node in the circle + * @return circle length + */ + def calculateCircleLength(node: Node): Int = { + var length = 1 + var cursor = node.next.get + + while (cursor != node) { + length += 1 + cursor = cursor.next.get + } + + length + } + + def findCircleEntrance(head: Node): Option[Node] = { + checkCircle(head).map(node => { + val length = calculateCircleLength(node) + var fast = head + var slow = head + //fast move length steps + for (i <- 0 until length) { + fast = fast.next.get + } + + while (slow != fast) { + fast = fast.next.get + slow = slow.next.get + } + + slow + }) + } + + //assuming nodeA and nodeB are all sorted list in ascending order + def mergeSortedList(nodeA: Option[Node], nodeB: Option[Node]): Option[Node] = { + if (nodeA.isEmpty && nodeB.isEmpty) { + return None + } + if (nodeA.isEmpty && nodeB.isDefined) { + return nodeB + } + if (nodeA.isDefined && nodeB.isEmpty) { + return nodeA + } + //now we have both nodeA and nodeB defined + var head: Option[Node] = None + var leftCursor = nodeA + var rightCursor = nodeB + + //calculate head and we are sure both leftCursor and rightCursor has data + if (leftCursor.get.data < rightCursor.get.data) { + head = leftCursor + leftCursor = leftCursor.get.next + } else { + head = rightCursor + rightCursor = rightCursor.get.next + } + + var mergedCursor: Option[Node] = head + while (leftCursor.isDefined && rightCursor.isDefined) { + if (leftCursor.get.data < rightCursor.get.data) { + mergedCursor.get.next = leftCursor + leftCursor = leftCursor.get.next + } else { + mergedCursor.get.next = rightCursor + rightCursor = rightCursor.get.next + } + mergedCursor = mergedCursor.get.next + } + //we have loop over at least one chain + //we just put the other chain in to the merged chain + if (leftCursor.isDefined) { + mergedCursor.get.next = leftCursor + } else { + mergedCursor.get.next = rightCursor + } + + head + } + + def deleteLastKthNode(headOpt: Option[Node], k: Int): Option[Node] = { + require(k > 0, "k must greater than 0") + headOpt match { + case None => None + case Some(head) => + var index = 0 + var slow = headOpt + var fast = headOpt + while (fast.get.next.isDefined && index < k) { + //move fast cursor to k + fast = fast.get.next + index += 1 + } + + if (fast.get.next.isEmpty && index + 1 == k) { + //deleting the head element + return head.next + } + + require(index.equals(k), "given linked list should contains at least k elements ") + while (fast.get.next.isDefined) { + fast = fast.get.next + slow = slow.get.next + } + + //fast cursor is in the end of the chain + //slow is the previous pos of k element + //do the operation + slow.get.next = slow.get.next.get.next + } + + headOpt + } + + //form all the chain value as string + def mkStringForChain(node: Node): String = { + val result = new StringBuilder + + var p = node + + while (p.next.isDefined) { + result.append(p.data) + p = p.next.get + } + result.append(p.data) + result.mkString + } +} diff --git a/scala/src/main/scala/ch08_stack/BrowserDemo.scala b/scala/src/main/scala/ch08_stack/BrowserDemo.scala new file mode 100644 index 00000000..555eb79d --- /dev/null +++ b/scala/src/main/scala/ch08_stack/BrowserDemo.scala @@ -0,0 +1,31 @@ +package ch08_stack + +class BrowserDemo(var currentPageOpt: Option[String], val backStack: StackDemo[String], + val forwardStack: StackDemo[String]) { + + def this() = this(None, new StackDemo[String], new StackDemo[String]) + + def open(page: String) = { + currentPageOpt.foreach(backStack.push) + forwardStack.clear() + currentPageOpt = Some(page) + } + + def canGoBack(): Boolean = backStack.size > 0 + + def goBack(): Unit = { + backStack.pop().foreach(page => { + forwardStack.push(currentPageOpt.get) + currentPageOpt = Some(page.data) + }) + } + + def canGoForward(): Boolean = forwardStack.size > 0 + + def goForward(): Unit = { + forwardStack.pop().foreach(page => { + backStack.push(currentPageOpt.get) + currentPageOpt = Some(page.data) + }) + } +} diff --git a/scala/src/main/scala/ch08_stack/StackDemo.scala b/scala/src/main/scala/ch08_stack/StackDemo.scala new file mode 100644 index 00000000..12a3a10b --- /dev/null +++ b/scala/src/main/scala/ch08_stack/StackDemo.scala @@ -0,0 +1,32 @@ +package ch08_stack + +class Node[T](var data: T, var next: Option[Node[T]]) + +class StackDemo[T] { + + + var headOpt: Option[Node[T]] = None + var size = 0 + + def clear(): Unit = { + headOpt = None + size = 0 + } + + def push(data: T) = { + val newHead = new Node(data, headOpt) + headOpt = Some(newHead) + size += 1 + } + + def pop(): Option[Node[T]] = { + headOpt match { + case None => None + case Some(head) => + headOpt = head.next + size -= 1 + Some(head) + + } + } +} diff --git a/scala/src/main/scala/ch09_queue/ArrayQueue.scala b/scala/src/main/scala/ch09_queue/ArrayQueue.scala new file mode 100644 index 00000000..c8a259b0 --- /dev/null +++ b/scala/src/main/scala/ch09_queue/ArrayQueue.scala @@ -0,0 +1,28 @@ +package ch09_queue + +import scala.reflect.ClassTag + +class ArrayQueue[T: ClassTag](capacity: Int) extends DemoQueue[T] { + + var items: Array[T] = new Array[T](capacity) + var head = 0 + var tail = 0 + + override def enqueue(data: T): Unit = { + require(tail < capacity, "queue is full") + items(tail) = data + tail += 1 + size += 1 + } + + override def dequeue(): Option[T] = { + if (head < tail) { + val result = Some(items(head)) + head += 1 + size -= 1 + result + } else { + None + } + } +} diff --git a/scala/src/main/scala/ch09_queue/CircularQueue.scala b/scala/src/main/scala/ch09_queue/CircularQueue.scala new file mode 100644 index 00000000..cfe77f64 --- /dev/null +++ b/scala/src/main/scala/ch09_queue/CircularQueue.scala @@ -0,0 +1,29 @@ +package ch09_queue + +import scala.reflect.ClassTag + +class CircularQueue[T: ClassTag](capacity: Int) extends DemoQueue[T] { + + var items: Array[T] = new Array[T](capacity) + var head = 0 + var tail = 0 + + + override def enqueue(data: T): Unit = { + require((tail + 1) % capacity != head, "queue is full") + items(tail) = data + tail = (tail + 1) % capacity + size += 1 + } + + override def dequeue(): Option[T] = { + if (head == tail) { + None + } else { + size -= 1 + val result = Some(items(head)) + head = (head + 1) % capacity + result + } + } +} diff --git a/scala/src/main/scala/ch09_queue/DemoQueue.scala b/scala/src/main/scala/ch09_queue/DemoQueue.scala new file mode 100644 index 00000000..197ac3e3 --- /dev/null +++ b/scala/src/main/scala/ch09_queue/DemoQueue.scala @@ -0,0 +1,10 @@ +package ch09_queue + +trait DemoQueue[T] { + + var size = 0 + + def enqueue(data: T): Unit + + def dequeue(): Option[T] +} diff --git a/scala/src/main/scala/ch09_queue/DynamicArrayQueue.scala b/scala/src/main/scala/ch09_queue/DynamicArrayQueue.scala new file mode 100644 index 00000000..50ac5b1a --- /dev/null +++ b/scala/src/main/scala/ch09_queue/DynamicArrayQueue.scala @@ -0,0 +1,19 @@ +package ch09_queue + +import scala.reflect.ClassTag + +class DynamicArrayQueue[T: ClassTag](val capacity: Int) extends ArrayQueue[T](capacity) { + + override def enqueue(data: T): Unit = { + if (tail == capacity) { + //tail is the end of + require(head > 0, "queue is full") + for (i <- Range(head, tail)) { + items(i - head) = items(head) + } + tail = tail - head + head = 0 + } + super.enqueue(data) + } +} diff --git a/scala/src/main/scala/ch09_queue/LinkedListQueue.scala b/scala/src/main/scala/ch09_queue/LinkedListQueue.scala new file mode 100644 index 00000000..52cb0fed --- /dev/null +++ b/scala/src/main/scala/ch09_queue/LinkedListQueue.scala @@ -0,0 +1,39 @@ +package ch09_queue + +class Node[T](var data: T, var next: Option[Node[T]]) + +class LinkListQueue[T]() extends DemoQueue[T] { + + var headOpt: Option[Node[T]] = None + var tailOpt: Option[Node[T]] = None + + override + def enqueue(data: T): Unit = { + val node = new Node(data, None) + size += 1 + headOpt match { + case None => headOpt = Some(node) + case Some(_) => + } + tailOpt match { + case None => tailOpt = Some(node) + case Some(tail) => + tail.next = Some(node) + tailOpt = Some(node) + } + } + + override + def dequeue(): Option[T] = { + headOpt.map(head => { + size -= 1 + headOpt = head.next + if (headOpt.isEmpty) { + //head is empty reach the end of the queue + tailOpt = None + } + head.data + }) + } + +} diff --git a/scala/src/main/scala/ch10_recursive/RecursiveDemo.scala b/scala/src/main/scala/ch10_recursive/RecursiveDemo.scala new file mode 100644 index 00000000..50613ebb --- /dev/null +++ b/scala/src/main/scala/ch10_recursive/RecursiveDemo.scala @@ -0,0 +1,23 @@ +package ch10_recursive + +import scala.collection.mutable + +object RecursiveDemo { + + def calculateStepWays(steps: Int): Int = { + //use knownResults to avoid duplicated computing + val knownResults = mutable.HashMap.empty[Int, Int] + steps match { + case 1 => 1 + case 2 => 2 + case _ => knownResults.get(steps) match { + case Some(result) => result + case None => { + val result = calculateStepWays(steps - 1) + calculateStepWays(steps - 2) + knownResults.put(steps, result) + result + } + } + } + } +} diff --git a/scala/src/main/scala/ch11_sorts/Sorts.scala b/scala/src/main/scala/ch11_sorts/Sorts.scala new file mode 100644 index 00000000..bc918e91 --- /dev/null +++ b/scala/src/main/scala/ch11_sorts/Sorts.scala @@ -0,0 +1,70 @@ +package ch11_sorts + +import scala.util.control.Breaks.{break, breakable} + +/** + * 冒泡排序、插入排序、选择排序 + * + * Author: yangchuz + */ +object Sorts { + + def bubbleSort(items: Array[Int]): Array[Int] = { + val length = items.length + breakable { + for (i <- Range(0, length)) { + var exit = true + for (j <- Range(0, length - i - 1)) { + if (items(j + 1) < items(j)) { + val temp = items(j + 1) + items(j + 1) = items(j) + items(j) = temp + exit = false + } + } + if (exit) { + break + } + } + } + items + } + + def insertSort(items: Array[Int]): Array[Int] = { + val length = items.length + for (i <- Range(1, length)) { + val value = items(i) + var j = i - 1 + breakable { + while (j >= 0) { + if (items(j) > value) { + items(j + 1) = items(j) + } else { + break + } + j -= 1 + } + } + items(j + 1) = value + } + items + } + + def selectionSort(items: Array[Int]): Array[Int] = { + val length = items.length + for (i <- Range(0, length)) { + var minIndex = i + for (j <- Range(i + 1, length)) { + if (items(j) < items(minIndex)) { + minIndex = j + } + } + + //put the min value to the front + val temp = items(i) + items(i) = items(minIndex) + items(minIndex) = temp + } + items + } +} diff --git a/scala/src/main/scala/ch12_sorts/MergeSort.scala b/scala/src/main/scala/ch12_sorts/MergeSort.scala new file mode 100644 index 00000000..27de8b0f --- /dev/null +++ b/scala/src/main/scala/ch12_sorts/MergeSort.scala @@ -0,0 +1,60 @@ +package ch12_sorts + +object MergeSort { + + def mergeSort(items: Array[Int]): Array[Int] = { + _mergeSort(items, 0, items.length - 1) + items + } + + + private[this] def _mergeSort(items: Array[Int], p: Int, r: Int): Unit = { + if (p >= r) { + return + } + + val q = p + (r - p) / 2 + _mergeSort(items, p, q) + _mergeSort(items, q + 1, r) + _merge(items, p, q, r) + + } + + private[this] def _merge(items: Array[Int], p: Int, q: Int, r: Int): Unit = { + //start of first half + var i = p + //start of second half + var j = q + 1 + var k = 0 + //temp array to hold the data + val tempArray = new Array[Int](r - p + 1) + while (i <= q && j <= r) { + if (items(i) <= items(j)) { + tempArray(k) = items(i) + i += 1 + } else { + tempArray(k) = items(j) + j += 1 + } + k += 1 + } + + var start = i + var end = q + + if (j <= r) { + start = j + end = r + } + + for (n <- start to end) { + tempArray(k) = items(n) + k += 1 + } + + //copy tempArray back to items + for (n <- 0 to r - p) { + items(p + n) = tempArray(n) + } + } +} diff --git a/scala/src/main/scala/ch12_sorts/QuickSort.scala b/scala/src/main/scala/ch12_sorts/QuickSort.scala new file mode 100644 index 00000000..afcea21c --- /dev/null +++ b/scala/src/main/scala/ch12_sorts/QuickSort.scala @@ -0,0 +1,54 @@ +package ch12_sorts + +object QuickSort { + + //find the K th smallest element int the array + def findKthElement(items: Array[Int], k: Int): Int = { + _findKthElement(items, k, 0, items.length - 1) + } + + private[this] def _findKthElement(items: Array[Int], k: Int, p: Int, r: Int): Int = { + val q = _partition(items, p, r) + + if (k == q + 1) { + items(q) + } else if (k < q + 1) { + _findKthElement(items, k, p, q - 1) + } else { + _findKthElement(items, k, q + 1, r) + } + } + + def quickSort(items: Array[Int]): Array[Int] = { + _quickSort(items, 0, items.length - 1) + items + } + + private[this] def _quickSort(items: Array[Int], p: Int, r: Int): Unit = { + if (p >= r) { + return + } + val q = _partition(items, p, r) + _quickSort(items, p, q - 1) + _quickSort(items, q + 1, r) + } + + private[this] def _partition(items: Array[Int], p: Int, r: Int): Int = { + val pivot = items(r) + var i = p + for (j <- Range(p, r)) { + if (items(j) < pivot) { + val temp = items(i) + items(i) = items(j) + items(j) = temp + i += 1 + } + } + + val temp = items(i) + items(i) = items(r) + items(r) = temp + + i + } +} diff --git a/scala/src/main/scala/ch15_bsearch/BSearch.scala b/scala/src/main/scala/ch15_bsearch/BSearch.scala new file mode 100644 index 00000000..9a9ef028 --- /dev/null +++ b/scala/src/main/scala/ch15_bsearch/BSearch.scala @@ -0,0 +1,50 @@ +package ch15_bsearch + +import scala.math.abs + +object BSearch { + + def search(items: Array[Int], target: Int): Int = { + var low = 0 + var high = items.length - 1 + while (low <= high) { + val mid = low + (high - low) / 2 + if (items(mid) == target) { + return mid + } else if (items(mid) > target) { + high = mid - 1 + } else { + low = mid + 1 + } + } + + -1 + } + + def sqrt(x: Double, precision: Double): Double = { + + require(precision > 0, "precision must > 0") + require(x > 0, "input value for sqrt must > 0") + var low = 0.0 + var high = x + val actualPrecision = precision / 10 + + if (x > 0 && x < 1) { + low = x + high = 1 + } + while (high - low > actualPrecision) { + val mid = low + (high - low) / 2 + if (abs(mid * mid - x) < actualPrecision) { + //find it + return mid + } else if (mid * mid > x) { + high = mid + } else { + low = mid + } + } + throw new IllegalStateException("could not determine the sqrt value for " + x) + + } +} diff --git a/scala/src/main/scala/ch15_bsearch/BSearchRecursive.scala b/scala/src/main/scala/ch15_bsearch/BSearchRecursive.scala new file mode 100644 index 00000000..8ea4010e --- /dev/null +++ b/scala/src/main/scala/ch15_bsearch/BSearchRecursive.scala @@ -0,0 +1,23 @@ +package ch15_bsearch + +object BSearchRecursive { + + def search(items: Array[Int], target: Int): Int = { + _search(items, target, 0, items.length - 1) + } + + private[this] def _search(items: Array[Int], target: Int, low: Int, high: Int): Int = { + if (low > high) { + return -1 + } + + val mid = low + (high - low) / 2 + if (items(mid) == target) { + mid + } else if (items(mid) > target) { + _search(items, target, low, mid - 1) + } else { + _search(items, target, mid + 1, high) + } + } +} diff --git a/scala/src/main/scala/ch16_bsearch/BSearch.scala b/scala/src/main/scala/ch16_bsearch/BSearch.scala new file mode 100644 index 00000000..74e2fbea --- /dev/null +++ b/scala/src/main/scala/ch16_bsearch/BSearch.scala @@ -0,0 +1,87 @@ +package ch16_bsearch + +object BSearch { + + //find the first index of given value + //-1 if not found + def findFirstValue(items: Array[Int], target: Int): Int = { + require(items.length > 0, "given array is empty") + var low = 0 + var high = items.length - 1 + while (low <= high) { + val mid = low + (high - low) / 2 + if (items(mid) > target) { + high = mid - 1 + } else if (items(mid) < target) { + low = mid + 1 + } else { + //find the value in the array + if (mid == 0 || items(mid - 1) != target) { + return mid + } else { + high = mid - 1 + } + } + } + -1 + } + + def findLastValue(items: Array[Int], target: Int): Int = { + var low = 0 + var high = items.length - 1 + while (low <= high) { + val mid = low + (high - low) / 2 + if (items(mid) > target) { + high = mid - 1 + } else if (items(mid) < target) { + low = mid + 1 + } else { + //find the target value + if (mid == items.length - 1 || items(mid + 1) != target) { + return mid + } else { + low = mid + 1 + } + } + } + -1 + } + + def findFirstGreaterThan(items: Array[Int], target: Int): Int = { + var low = 0 + var high = items.length + while (low <= high) { + val mid = low + (high - low) / 2 + if (items(mid) >= target) { + //find the range + if (mid == 0 || items(mid - 1) < target) { + return mid + } else { + high = mid - 1 + } + } else { + low = mid + 1 + } + } + -1 + } + + def findLastSmallerThan(items: Array[Int], target: Int): Int = { + var low = 0 + var high = items.length - 1 + while (low <= high) { + var mid = low + (high - low) / 2 + if (items(mid) <= target) { + //find the range + if (mid == items.length - 1 || items(mid + 1) > target) { + return mid + } else { + low = mid + 1 + } + } else { + high = mid - 1 + } + } + -1 + } +} diff --git a/scala/src/main/scala/ch17_skip_list/SkipList.scala b/scala/src/main/scala/ch17_skip_list/SkipList.scala new file mode 100644 index 00000000..23791150 --- /dev/null +++ b/scala/src/main/scala/ch17_skip_list/SkipList.scala @@ -0,0 +1,99 @@ +package ch17_skip_list + +import scala.util.Random + +class Node(var data: Int, var forwards: Array[Node], var maxLevel: Int) + +class SkipList(var head: Node, var skipListLevel: Int) { + + def this() = this(new Node(-1, new Array[Node](16), 0), 1) + + val MAX_LEVEL = 16 + val random = new Random() + + def find(value: Int): Option[Node] = { + var p = head + for (i <- skipListLevel - 1 to 0 by -1) { + while (p.forwards(i) != null && p.forwards(i).data < value) { + p = p.forwards(i) + } + } + if (p.forwards(0) != null && p.forwards(0).data == value) { + Some(p.forwards(0)) + } else { + None + } + } + + def insert(value: Int): Unit = { + //init the new node + val level = randomLevel() + val newNode = new Node(value, new Array[Node](level), level) + + //use updtes array to record all nodes in all level before the inserted node + val updates: Array[Node] = new Array[Node](level) + var p = head + for (i <- level - 1 to 0 by -1) { + while (p.forwards(i) != null && p.forwards(i).data < value) { + p = p.forwards(i) + } + updates(i) = p + } + + for (i <- Range(0, level)) { + newNode.forwards(i) = updates(i).forwards(i) + updates(i).forwards(i) = newNode + } + + if (level > skipListLevel) { + skipListLevel = level + } + } + + def delete(value: Int): Unit = { + var p = head + val updates: Array[Node] = new Array[Node](skipListLevel) + + //try to locate the given node with the value + for (i <- skipListLevel - 1 to 0 by -1) { + while (p.forwards(i) != null && p.forwards(i).data < value) { + p = p.forwards(i) + } + updates(i) = p + } + + if (p.forwards(0) != null && p.forwards(0).data == value) { + //find the value node, start to delete the node from the skip list + for (i <- skipListLevel - 1 to 0 by -1) { + if (updates(i).forwards(i) != null && updates(i).forwards(i).data == value) { + updates(i).forwards(i) = updates(i).forwards(i).forwards(i) + } + } + } + + } + + def randomLevel(): Int = { + var level = 1 + for (i <- Range(1, MAX_LEVEL)) { + if (random.nextInt() % 2 == 1) { + level += 1 + } + } + + level + } + + def mkString(): String = { + val builder = new StringBuilder + var p = head + while (p.forwards(0) != null) { + p = p.forwards(0) + builder.append(p.data) + } + + builder.mkString + } +} + + diff --git a/scala/src/main/scala/ch20_linked_hash_map/LRUCache.scala b/scala/src/main/scala/ch20_linked_hash_map/LRUCache.scala new file mode 100644 index 00000000..ec64fdfe --- /dev/null +++ b/scala/src/main/scala/ch20_linked_hash_map/LRUCache.scala @@ -0,0 +1,149 @@ +package ch20_linked_hash_map + +class Node[K, V](var key: Option[K], var data: Option[V], var prev: Option[Node[K, V]], var next: Option[Node[K, V]], + var hNext: Option[Node[K, V]]) { + + def this(key: Option[K], data: Option[V]) = this(key, data, None, None, None) +} + +/** + * LRU cache - https://leetcode.com/problems/lru-cache/ see unit test from LRUCacheTest + * + * @author email2liyang@gmail.com + */ +class LRUCache[K, V](var head: Node[K, V], var tail: Node[K, V], var table: Array[Node[K, V]], + capacity: Int = 1000, var elementCount: Int = 0) { + + head.next = Some(tail) + tail.prev = Some(head) + + def this(capacity: Int) = this(new Node(None, None), new Node(None, None), new Array[Node[K, V]](capacity), capacity) + + def get(key: K): Option[V] = { + val index = indexFor(key.hashCode()) + var hNode = table(index) + if (hNode == null) { + None + } else { + while (!hNode.key.get.equals(key) && hNode.hNext.isDefined) { + hNode = hNode.hNext.get + } + if (hNode.key.get.equals(key)) { + //move this to the end of the linked list + moveHNodeToTail(hNode) + hNode.data + } else { + None + } + } + } + + + //put data into the linked hash map + //1: check if the data exist in the linked list + //2: if it's not exist , append it in the linked list + //3: if it's exist in the list, move it to the tail of the linked list + //4: return old value if it's exist + def put(key: K, value: V): Option[V] = { + + if (elementCount == capacity) { + deleteLRUElement() + } + + val node = new Node(Some(key), Some(value)) + val index = indexFor(key.hashCode()) + var hNode = table(index) + var result: Option[V] = None + if (hNode == null) { + //if it's not exist , append it in the linked list + node.prev = tail.prev + node.next = Some(tail) + tail.prev.get.next = Some(node) + tail.prev = Some(node) + table(index) = node + elementCount += 1 + } else { + //we find a key conflict in the hash table + //start to loop the hNode to match the key + while (!hNode.key.get.equals(key) && hNode.hNext.isDefined) { + hNode = hNode.hNext.get + } + + if (hNode.key.get.equals(key)) { + //find the old data from the hash table + result = hNode.data + hNode.data = Some(value) + //move the node to the tail of the linked list + moveHNodeToTail(hNode) + //hNext pointer stay untouched + } else { + //could not find the old data + //put the new node into the tail of the linked list + node.prev = tail.prev + node.next = Some(tail) + tail.prev.get.next = Some(node) + tail.prev = Some(node) + + //put it the tail of the hash table's list + //iterator to the end of hNode + while (hNode.hNext.isDefined) { + hNode = hNode.hNext.get + } + hNode.hNext = Some(node) + elementCount += 1 + } + } + result + } + + private[this] def moveHNodeToTail(hNode: Node[K, V]) = { + hNode.prev.get.next = hNode.next + hNode.next.get.prev = hNode.prev + hNode.prev = tail.prev + hNode.next = Some(tail) + tail.prev.get.next = Some(hNode) + tail.prev = Some(hNode) + } + + private[this] def deleteLRUElement(): Unit = { + //cache is full, start to delete element from the head + val node = head.next.get + + //delete it from head + node.next.get.prev = Some(head) + head.next = node.next + + //deal with hNext in the table + val index = indexFor(node.key.get.hashCode()) + var hNode = table(index) + //deal with first element in the hash table + if (hNode.key.get.equals(node.key.get)) { + hNode.hNext match { + case Some(n) => table(index) = n + case None => table(index) = null + } + } else { + //deal with not first element in the hash table + var hNodePrev = hNode + hNode = hNode.next.get + while (!hNode.key.get.equals(node.key.get)) { + hNode = hNode.next.get + hNodePrev = hNodePrev.next.get + } + //now hNodePrev is the previous hNode in the hashtable + //remove the hNode + hNodePrev.next = hNode.next + + hNode.next match { + case Some(n) => n.prev = Some(hNodePrev) + case None => + } + } + + elementCount -= 1 + } + + private[this] def indexFor(hash: Int): Int = { + hash % table.length + } +} diff --git a/scala/src/main/scala/ch23_binary_tree/BinaryTree.scala b/scala/src/main/scala/ch23_binary_tree/BinaryTree.scala new file mode 100644 index 00000000..3dfed76c --- /dev/null +++ b/scala/src/main/scala/ch23_binary_tree/BinaryTree.scala @@ -0,0 +1,61 @@ +package ch23_binary_tree + +import scala.collection.mutable + +class Node[T](var data: T, var left: Option[Node[T]], var right: Option[Node[T]]) + +class BinaryTree[T] { + + def preOrder(root: Option[Node[T]]): String = { + val result = new StringBuilder + + if (root.isDefined) { + result.append(root.map(_.data.toString).get) + result.append(preOrder(root.get.left)) + result.append(preOrder(root.get.right)) + } + result.mkString + } + + def inOrder(root: Option[Node[T]]): String = { + val result = new StringBuilder + + if (root.isDefined) { + result.append(inOrder(root.get.left)) + result.append(root.map(_.data.toString).get) + result.append(inOrder(root.get.right)) + } + result.mkString + } + + def postOrder(root: Option[Node[T]]): String = { + val result = new StringBuilder + + if (root.isDefined) { + result.append(postOrder(root.get.left)) + result.append(postOrder(root.get.right)) + result.append(root.map(_.data.toString).get) + } + result.mkString + } + + def levelOrder(root: Option[Node[T]]): String = { + val result = new StringBuilder + val queue = new mutable.Queue[Node[T]]() + if (root.isDefined) { + queue += root.get + while (!queue.isEmpty) { + val node = queue.dequeue() + result.append(node.data.toString) + if (node.left.isDefined) { + queue += node.left.get + } + if (node.right.isDefined) { + queue += node.right.get + } + } + } + + result.mkString + } +} diff --git a/scala/src/main/scala/ch24_binary_search_tree/BinarySearchTree.scala b/scala/src/main/scala/ch24_binary_search_tree/BinarySearchTree.scala new file mode 100644 index 00000000..51e381f9 --- /dev/null +++ b/scala/src/main/scala/ch24_binary_search_tree/BinarySearchTree.scala @@ -0,0 +1,148 @@ +package ch24_binary_search_tree + +import ch23_binary_tree.{BinaryTree, Node} + +import scala.util.control.Breaks._ + +class BinarySearchTree(var root: Option[Node[Int]]) extends BinaryTree[Int] { + + def insert(data: Int): Node[Int] = { + val dataNode = new Node(data, None, None) + root match { + case None => root = Some(dataNode) + case Some(_) => { + var p = root + breakable { + while (p.isDefined) { + if (data > p.get.data) { + p.get.right match { + case None => { + p.get.right = Some(dataNode) + break + } + case Some(_) => p = p.get.right + } + } + if (data < p.get.data) { + p.get.left match { + case None => { + p.get.left = Some(dataNode) + break + } + case Some(_) => p = p.get.left + } + } + } + } + } + } + + dataNode + } + + def find(data: Int): Option[Node[Int]] = { + var p = root + breakable { + while (p.isDefined) { + if (data > p.get.data) { + p = p.get.right + } else if (data < p.get.data) { + p = p.get.left + } else { + //find the value + break + } + } + } + p + } + + def delete(data: Int): Unit = { + //there are 3 scenarios + //1: data is leaf node + //2: data has one child node + //3: data has two child nodes, we need to find out the smallest node from right branch + var p = root + var pp: Option[Node[Int]] = None //parent node of deleted node + + //find matching node to delete + breakable { + while (p.isDefined) { + if (data > p.get.data) { + pp = p + p = p.get.right + } else if (data < p.get.data) { + pp = p + p = p.get.left + } else { + //find the value + break + } + } + } + + if (p.isEmpty) { + //find nothing + return + } + + //now we find the node to delete + //scenario 3 + if (p.get.left.isDefined && p.get.right.isDefined) { + //need to find out the smallest node in right branch + var minPP = p + var minP = p.get.right + while (minP.get.left.isDefined) { + minPP = minP + minP = minP.get.left + } + + //assign the smallest value in the right branch to the node to be deleted + p.get.data = minP.get.data + //now problems becomes delete the minP in the tree + //minP must not have any left child node + //minP may or may not have right child node + //it will fall back to scenario 1 or 2 + p = minP + pp = minPP + } + + //child is the child of p + var child: Option[Node[Int]] = None + if (p.get.left.isDefined) { + child = p.get.left + } else if (p.get.right.isDefined) { + child = p.get.right + } + + //starting the node deletion + pp match { + case None => root = child + case Some(parentNode) => { + if (parentNode.left == p) { + parentNode.left = child + } else if (parentNode.right == p) { + parentNode.right = child + } + } + } + + } + + def height(): Int = { + _height(root) + } + + private[this] def _height(nodeOpt: Option[Node[Int]]): Int = { + nodeOpt match { + case None => 0 + case Some(node) => { + if (node.left.isEmpty && node.right.isEmpty) { + 1 + } else { + scala.math.max(_height(node.left), _height(node.right)) + 1 + } + } + } + } +} diff --git a/scala/src/main/scala/ch28_heap/Heap.scala b/scala/src/main/scala/ch28_heap/Heap.scala new file mode 100644 index 00000000..f5f43b6a --- /dev/null +++ b/scala/src/main/scala/ch28_heap/Heap.scala @@ -0,0 +1,95 @@ +package ch28_heap + +import scala.util.control.Breaks._ + +class Heap(val capacity: Int, var elementCount: Int = 0) { + + def this(arrayParam: Array[Int], bottomUp: Boolean) = { + this(arrayParam.length + 1) + if (bottomUp) { + arrayParam.foreach(this.insert) + } else { + //copy data into array of heap + for (i <- arrayParam.indices) { + array(i + 1) = arrayParam(i) + elementCount = arrayParam.length + } + for (i <- elementCount / 2 + 1 to 1 by -1) { + heapifyTopDown(i, elementCount - 1) + } + } + } + + require(capacity > 0, "capacity should be > 0") + val array: Array[Int] = new Array[Int](capacity) + + def insert(data: Int): Unit = { + if (elementCount == capacity - 1) { + throw new IllegalStateException("heap full") + } + + elementCount += 1 + array(elementCount) = data + + //heapify bottom up + //compare the element with it's parent node i/2 until parent node > child node + //this will make sure the root element of the tree is the biggest value + var i = elementCount + while (i / 2 > 0 && array(i) > array(i / 2)) { + val temp = array(i) + array(i) = array(i / 2) + array(i / 2) = temp + i = i / 2 + } + + } + + def removeMax(): Int = { + require(elementCount > 0, "heap is empty") + val result = array(1) + array(1) = array(elementCount) + elementCount -= 1 + heapifyTopDown(1, elementCount) + result + } + + //heapify from top to bottom + //start from the top to compare with it's child nodes + //swap if child node > parent node + //stop at child node <= parent node + private[this] def heapifyTopDown(startIndex: Int, stopIndex: Int) = { + var pointer = startIndex + breakable { + while (true) { + var maxPos = pointer + if (pointer * 2 <= stopIndex && array(pointer * 2) > array(maxPos)) { + maxPos = pointer * 2 + } + if (pointer * 2 <= stopIndex && array(pointer * 2 + 1) > array(maxPos)) { + maxPos = pointer * 2 + 1 + } + if (maxPos == pointer) { + break + } + //swap the parent and child + val temp = array(pointer) + array(pointer) = array(maxPos) + array(maxPos) = temp + //start a new round + pointer = maxPos + } + } + } + +} + +object Heap { + def heapSort(array: Array[Int]): Array[Int] = { + val result = new Array[Int](array.length) + val heap = new Heap(array, true) + for (i <- result.length - 1 to 0 by -1) { + result(i) = heap.removeMax() + } + result + } +} \ No newline at end of file diff --git a/scala/src/main/scala/ch29_heap_solutions/FileMerger.scala b/scala/src/main/scala/ch29_heap_solutions/FileMerger.scala new file mode 100644 index 00000000..a69fd4f2 --- /dev/null +++ b/scala/src/main/scala/ch29_heap_solutions/FileMerger.scala @@ -0,0 +1,46 @@ +package ch29_heap_solutions + +import java.io.{BufferedWriter, File, FileWriter} + +import scala.collection.mutable +import scala.io.Source +import scala.util.control.Breaks._ + +object FileMerger { + + /** + * each given file has sorted String as content, we need to merge them together + * + * @param smallFiles - small files with sorted content + * @return merged file + */ + def mergeFiles(smallFiles: List[File]): File = { + //init output file + val output = File.createTempFile("merged-file", ".txt") + val writer = new BufferedWriter(new FileWriter(output)) + //init small top heap + val priorityQueue = new mutable.PriorityQueue[(Char, Source)]()(Ordering.by((_: (Char, Source))._1).reverse) + val sources = smallFiles.toArray.map(smallFile => Source.fromFile(smallFile)) + //init fill the priority queue from each file + sources.foreach(source => priorityQueue.enqueue((source.next(), source))) + + breakable { + while (true) { + val next = priorityQueue.dequeue() + val output: Char = next._1 + val source = next._2 + writer.append(output) + if (source.hasNext) { + priorityQueue.enqueue((source.next(), source)) + } + //determine the end of merge + if (sources.forall(!_.hasNext) && priorityQueue.isEmpty) { + break + } + } + } + + writer.close() + output + } +} diff --git a/scala/src/main/scala/ch29_heap_solutions/MiddleNumberKeeper.scala b/scala/src/main/scala/ch29_heap_solutions/MiddleNumberKeeper.scala new file mode 100644 index 00000000..1bc243d1 --- /dev/null +++ b/scala/src/main/scala/ch29_heap_solutions/MiddleNumberKeeper.scala @@ -0,0 +1,42 @@ +package ch29_heap_solutions + +import scala.collection.mutable + +class MiddleNumberKeeper(val percent: Double) { + + def this() = this(0.5) + + val bigTop = new mutable.PriorityQueue[Int]() + val smallTop = new mutable.PriorityQueue[Int]()(scala.math.Ordering.Int.reverse) + + + def put(num: Int): Unit = { + if (smallTop.nonEmpty && num >= smallTop.head) { + smallTop += num + adjustHeap() + return + } + + //for any other scenario, we just put the item to bitTop then adjustHeap + bigTop += num + adjustHeap() + } + + def get(): Option[Int] = { + bigTop.headOption + } + + private[this] def adjustHeap(): Unit = { + val totalLength = smallTop.length + bigTop.length + //deal with bigTop + while (bigTop.length.doubleValue() / totalLength - percent > 0.0001) { + //move item from bigTop to smallTop + smallTop += bigTop.dequeue() + } + + //deal with smallTop + while (smallTop.length.doubleValue() / totalLength - (1.0D - percent) > 0.0001) { + bigTop += smallTop.dequeue() + } + } +} diff --git a/scala/src/main/scala/ch29_heap_solutions/TopKItemsKeeper.scala b/scala/src/main/scala/ch29_heap_solutions/TopKItemsKeeper.scala new file mode 100644 index 00000000..88eba565 --- /dev/null +++ b/scala/src/main/scala/ch29_heap_solutions/TopKItemsKeeper.scala @@ -0,0 +1,30 @@ +package ch29_heap_solutions + +import scala.collection.mutable + +/** + * keep the top k items in the the class + */ +class TopKItemsKeeper(val itemsToKeepCount: Int) { + + + //have a smallest value top heap + val queue = new mutable.PriorityQueue[Int]()(scala.math.Ordering.Int.reverse) + + def put(item: Int): Unit = { + if (queue.length < itemsToKeepCount) { + queue += item + return + } + + //queue already have the k items + if (item.compareTo(queue.head) > 0) { + queue.dequeue() + queue += item + } + } + + def get(): List[Int] = { + queue.clone().dequeueAll + } +} diff --git a/scala/src/main/scala/ch31_graph/Graph.scala b/scala/src/main/scala/ch31_graph/Graph.scala new file mode 100644 index 00000000..a9f75be6 --- /dev/null +++ b/scala/src/main/scala/ch31_graph/Graph.scala @@ -0,0 +1,89 @@ +package ch31_graph + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.util.control.Breaks._ + +class Graph(vertex: Array[String]) { + + require(vertex.nonEmpty, "nonEmpty vertex required") + val adjacency = new Array[mutable.MutableList[String]](vertex.length) + for (i <- Range(0, vertex.length)) { + adjacency(i) = new mutable.MutableList[String]() + } + + def addEdge(startNode: String, endNode: String): Unit = { + adjacency(vertex.indexOf(startNode)) += endNode + adjacency(vertex.indexOf(endNode)) += startNode + } + + def getEdges(node: String): Array[String] = { + adjacency(vertex.indexOf(node)).toArray + } + + def breathFirstSearch(startNode: String, destNode: String): Option[Array[String]] = { + var result: Option[Array[String]] = None + val queue = new mutable.Queue[String]() + val visited = new mutable.HashSet[String]() + val explored = new ArrayBuffer[String]() + + //put starting node into the queue + queue += startNode + breakable { + while (queue.nonEmpty) { + val node = queue.dequeue() + if (!visited.contains(node)) { + explored += node + visited += node + + if (node.equals(destNode)) { + result = Some(explored.toArray) + break + } + queue ++= adjacency(vertex.indexOf(node)) + } + + } + } + + result + } + + def depthFirstSearch(startNode: String, destNode: String): Option[Array[String]] = { + var found = false + val visited = new mutable.HashSet[String]() + val explored = new ArrayBuffer[String]() + + def _dfs(start: String): Unit = { + if (found) { + return + } + if (!visited.contains(start)) { + visited += start + explored += start + val destsForStart: mutable.MutableList[String] = adjacency(vertex.indexOf(start)) + + breakable { + for (i <- destsForStart.indices) { + val node = destsForStart(i) + if (node.equals(destNode)) { + found = true + if (!explored.contains(node)) { + explored += node + } + break() + } + _dfs(node) + } + } + } + } + + _dfs(startNode) + if (found) { + Some(explored.toArray) + } else { + None + } + } +} diff --git a/scala/src/main/scala/ch32_matching/BruteForce.scala b/scala/src/main/scala/ch32_matching/BruteForce.scala new file mode 100644 index 00000000..ad612260 --- /dev/null +++ b/scala/src/main/scala/ch32_matching/BruteForce.scala @@ -0,0 +1,24 @@ +package ch32_matching + +import scala.util.control.Breaks._ + +object BruteForce { + + def firstIndexOf(main: Array[Char], sub: Array[Char]): Int = { + + require(main != null, "main array required") + require(sub != null, "sub array required") + require(main.length >= sub.length, "sub array should be small than main array") + var result = -1 + breakable { + for (i <- 0 until (main.length - sub.length)) { + if (main.slice(i, i + sub.length) sameElements sub) { + result = i + break + } + } + } + result + } + +} diff --git a/scala/src/main/scala/ch32_matching/RabinKarp.scala b/scala/src/main/scala/ch32_matching/RabinKarp.scala new file mode 100644 index 00000000..c7c9736c --- /dev/null +++ b/scala/src/main/scala/ch32_matching/RabinKarp.scala @@ -0,0 +1,39 @@ +package ch32_matching + +import scala.util.control.Breaks.{break, breakable} + +object RabinKarp { + + def firstIndexOf(main: Array[Char], sub: Array[Char]): Int = { + require(main != null, "main array required") + require(sub != null, "sub array required") + require(main.length >= sub.length, "sub array should be small than main array") + val baseNums: Array[Long] = new Array[Long](sub.length) + for (i <- sub.indices) { + baseNums(i) = scala.math.pow(128, i).longValue() + } + + val subHash = hash(sub, baseNums) + var result = -1 + breakable { + for (i <- 0 until (main.length - sub.length)) { + if (hash(main.slice(i, i + sub.length), baseNums).equals(subHash) + && main.slice(i, i + sub.length).sameElements(sub)) { + result = i + break + } + } + } + result + + } + + def hash(array: Array[Char], baseNums: Array[Long]): Long = { + var hash = 0L + for (i <- array.indices) { + hash += array(i).toInt * baseNums(array.length - 1 - i) + } + hash + } + +} diff --git a/scala/src/main/scala/ch35_tire_tree/TrieTree.scala b/scala/src/main/scala/ch35_tire_tree/TrieTree.scala new file mode 100644 index 00000000..2d3ac478 --- /dev/null +++ b/scala/src/main/scala/ch35_tire_tree/TrieTree.scala @@ -0,0 +1,106 @@ +package ch35_tire_tree + +import scala.collection.mutable.ArrayBuffer +import scala.util.control.Breaks._ + +class TrieNode(var data: Char, var children: Array[TrieNode], var isEndNode: Boolean = false) { + + def this(data: Char) = this(data, new Array[TrieNode](26)) + +} + +class TrieTree { + + val root = new TrieNode('/') //root node without any data + + def insert(text: Array[Char]): Unit = { + var p = root + for (i <- text.indices) { + val index = text(i) - 'a' + if (p.children(index) == null) { + val node = new TrieNode(text(i)) + p.children(index) = node + } + p = p.children(index) + } + //now p is pointing the leaf node + p.isEndNode = true + } + + def find(text: Array[Char]): Boolean = { + var p = root + + breakable { + for (i <- text.indices) { + val index = text(i) - 'a' + if (p.children(index) == null) { + break + } + p = p.children(index) + } + } + + p.isEndNode + } + + def prefix(prefix: Array[Char]): Boolean = { + var p = root + var level = -1 + breakable { + for (i <- prefix.indices) { + val index = prefix(i) - 'a' + if (p.children(index) == null) { + break + } + p = p.children(index) + level = i + } + } + + //should not reach leaf node and should have at least 1 matching level + !p.isEndNode && level > -1 + } + + def suggestion(text: Array[Char]): Option[Array[String]] = { + var p = root + var level = -1 + breakable { + for (i <- text.indices) { + val index = text(i) - 'a' + if (p.children(index) == null) { + break + } + p = p.children(index) + level = i + } + } + //have prefix matching + if (!p.isEndNode && level > -1) { + //now the problem becomes print all the children from p + Some(_children(p).map(str => "%s%s".format(text.slice(0, text.length - 1).mkString(""), str)).toArray) + } else { + None + } + + } + + def _children(p: TrieNode): ArrayBuffer[String] = { + if (p.isEndNode) { + return ArrayBuffer(p.data.toString) + } + + //p is not leaf node + val arrayBuffer = new ArrayBuffer[String]() + for (i <- p.children.indices) { + if (p.children(i) != null) { + _children(p.children(i)).foreach(subStr => + arrayBuffer.append("%s%s".format(p.data.toString, subStr)) + ) + } + } + + arrayBuffer + } + + +} diff --git a/scala/src/main/scala/ch39_back_tracking/BagWeight.scala b/scala/src/main/scala/ch39_back_tracking/BagWeight.scala new file mode 100644 index 00000000..bfc5c21d --- /dev/null +++ b/scala/src/main/scala/ch39_back_tracking/BagWeight.scala @@ -0,0 +1,25 @@ +package ch39_back_tracking + +class BagWeight(maxBagItemCount: Int, maxBagWeight: Int) { + + def calculateMaxWeight(items: Array[Int]): Int = { + var maxWeight = 0 + + def _calcMaxWeight(itemIndex: Int, currentWeight: Int): Unit = { + if (currentWeight == maxBagWeight || itemIndex == items.length) { + if (currentWeight > maxWeight) { + maxWeight = currentWeight + } + } else { + //check next item + _calcMaxWeight(itemIndex + 1, currentWeight) + if (currentWeight + items(itemIndex) <= maxBagWeight) { + _calcMaxWeight(itemIndex + 1, currentWeight + items(itemIndex)) + } + } + } + + _calcMaxWeight(0, 0) + maxWeight + } +} diff --git a/scala/src/main/scala/ch39_back_tracking/EightQueens.scala b/scala/src/main/scala/ch39_back_tracking/EightQueens.scala new file mode 100644 index 00000000..fbb6d8a8 --- /dev/null +++ b/scala/src/main/scala/ch39_back_tracking/EightQueens.scala @@ -0,0 +1,78 @@ +package ch39_back_tracking + +import scala.util.control.Breaks._ + +class EightQueens { + + //use array index to identify the row,the value of the row to identify the column + val result = new Array[Int](8) + var count = 0 + + def calc8Queues(row: Int): Unit = { + if (row == 8) { + //everything is done + print8Queens() + return + } + + for (column <- Range(0, 8)) { + if (isOkOnColumn(row, column)) { + result(row) = column //place the column value into the array + calc8Queues(row + 1) //calculate next row + } + } + + } + + def isOkOnColumn(row: Int, column: Int): Boolean = { + var ok = true + var leftUp = column - 1 + var rightUp = column + 1 + + breakable { + //will compare all the rows above current row + for (i <- row - 1 to 0 by -1) { + //check current column + if (result(i) == column) { + ok = false + break + } + //check left up + if (leftUp >= 0) { + if (result(i) == leftUp) { + ok = false + break + } + } + //check right up + if (rightUp < 8) { + if (result(i) == rightUp) { + ok = false + break + } + } + //move leftUp and rightUp + leftUp -= 1 + rightUp += 1 + } + } + + ok + } + + def print8Queens(): Unit = { + count +=1 + for (row <- Range(0, 8)) { + for (column <- Range(0, 8)) { + if (result(row) == column) { + print("Q ") + } else { + print("* ") + } + } + //new line for next row + println("") + } + println(count+"==============") + } +} diff --git a/scala/src/main/scala/ch39_back_tracking/NQueens.scala b/scala/src/main/scala/ch39_back_tracking/NQueens.scala new file mode 100644 index 00000000..b600d22f --- /dev/null +++ b/scala/src/main/scala/ch39_back_tracking/NQueens.scala @@ -0,0 +1,77 @@ +package ch39_back_tracking + +import scala.util.control.Breaks.{break, breakable} + +class NQueens(numberOfQueens:Int) { + //use array index to identify the row,the value of the row to identify the column + val result = new Array[Int](numberOfQueens) + var count = 0 + + def calcNQueues(row: Int): Unit = { + if (row == numberOfQueens) { + //everything is done + printNQueens() + return + } + + for (column <- Range(0, numberOfQueens)) { + if (isOkOnColumn(row, column)) { + result(row) = column //place the column value into the array + calcNQueues(row + 1) //calculate next row + } + } + + } + + def isOkOnColumn(row: Int, column: Int): Boolean = { + var ok = true + var leftUp = column - 1 + var rightUp = column + 1 + + breakable { + //will compare all the rows above current row + for (i <- row - 1 to 0 by -1) { + //check current column + if (result(i) == column) { + ok = false + break + } + //check left up + if (leftUp >= 0) { + if (result(i) == leftUp) { + ok = false + break + } + } + //check right up + if (rightUp < numberOfQueens) { + if (result(i) == rightUp) { + ok = false + break + } + } + //move leftUp and rightUp + leftUp -= 1 + rightUp += 1 + } + } + + ok + } + + def printNQueens(): Unit = { + count +=1 + for (row <- Range(0, numberOfQueens)) { + for (column <- Range(0, numberOfQueens)) { + if (result(row) == column) { + print("Q ") + } else { + print("* ") + } + } + //new line for next row + println("") + } + println(count+"==============") + } +} diff --git a/scala/src/main/scala/ch39_back_tracking/Sudoku.scala b/scala/src/main/scala/ch39_back_tracking/Sudoku.scala new file mode 100644 index 00000000..64b7d747 --- /dev/null +++ b/scala/src/main/scala/ch39_back_tracking/Sudoku.scala @@ -0,0 +1,113 @@ +package ch39_back_tracking + +import scala.util.control.Breaks._ + +class Sudoku { + + def resolve(grid: Array[Array[Int]]): Unit = { + printGrid(grid) + println("") + if (resolve(grid, 0, 0)) { + printGrid(grid) + } else { + println("no result") + printGrid(grid) + } + } + + private[this] def resolve(grid: Array[Array[Int]], row: Int, column: Int): Boolean = { + if (row == 8 && column == 9) { + //find the result + return true + } + + if (column == 9) { + //move to next line + return resolve(grid, row + 1, 0) + } + + if (grid(row)(column) != 0) { + //given number, resolve next one + return resolve(grid, row, column + 1) + } + + //start the real resolve + for (num <- 1 to 9) { + if (isOk(grid, row, column, num)) { + grid(row)(column) = num + if (resolve(grid, row, column + 1)) { + return true + } + } + } + + //do not find anything, reset given row and column + grid(row)(column) = 0 + false + } + + def isOk(grid: Array[Array[Int]], row: Int, column: Int, num: Int): Boolean = { + isRowOk(grid, row, num) && isColumnOk(grid, column, num) && isSmallBoxOk(grid, row, column, num) + } + + def isRowOk(grid: Array[Array[Int]], row: Int, num: Int): Boolean = { + var isOk = true + breakable { + for (column <- Range(0, 9)) { + if (grid(row)(column) == num) { + isOk = false + break + } + } + } + isOk + } + + def isColumnOk(grid: Array[Array[Int]], column: Int, num: Int): Boolean = { + var isOk = true + breakable { + for (row <- Range(0, 9)) { + if (grid(row)(column) == num) { + isOk = false + break + } + } + } + isOk + } + + def isSmallBoxOk(grid: Array[Array[Int]], row: Int, column: Int, num: Int): Boolean = { + val rowOffSet = (row / 3) * 3 + val columnOffSet = (column / 3) * 3 + var isOk = true + breakable { + for (i <- Range(0, 3)) { + for (j <- Range(0, 3)) { + if (grid(i + rowOffSet)(j + columnOffSet) == num) { + isOk = false + break + } + } + } + } + isOk + } + + def printGrid(grid: Array[Array[Int]]): Unit = { + for (i <- Range(0, 9)) { + if (i % 3 == 0) { + println("-------------------------") + } + for (j <- Range(0, 9)) { + if (j % 3 == 0) { + print("| ") + } + print(grid(i)(j) + " ") + } + println("| ") + + } + println("-------------------------") + } + +} diff --git a/scala/src/main/scala/ch43_topology_sort/GraphTopology.scala b/scala/src/main/scala/ch43_topology_sort/GraphTopology.scala new file mode 100644 index 00000000..c7aaeb53 --- /dev/null +++ b/scala/src/main/scala/ch43_topology_sort/GraphTopology.scala @@ -0,0 +1,100 @@ +package ch43_topology_sort + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + +class GraphTopology(vertex: Int) { + + //define the graph + val adjacency = new Array[mutable.MutableList[Int]](vertex) + for (i <- Range(0, vertex)) { + adjacency(i) = new mutable.MutableList[Int]() + } + + def addEdge(startIndex: Int, targetIndex: Int) = { + adjacency(startIndex) += targetIndex + } + + def topologySortByKahn(): Array[Int] = { + val seq = new mutable.ArrayBuffer[Int]() + //inDegrees contains all the inDegree for a given node + val inDegrees = new Array[Int](vertex) + for (i <- Range(0, vertex)) { + for (j <- adjacency(i).indices) { + val index = adjacency(i).get(j).get + inDegrees(index) += 1 + } + } + + val queue = new mutable.Queue[Int]() + for (i <- inDegrees.indices) { + if (inDegrees(i) == 0) { + // means there is no inDegree for this node, + // this could be the starting point of the dependency graph + queue += i + } + } + + //start to navigating the graph from the starting point + while (queue.nonEmpty) { + val index = queue.dequeue() + + //push to the result + seq += index + + for (i <- adjacency(index).indices) { + val inDegreeIndex = adjacency(index).get(i).get + inDegrees(inDegreeIndex) -= 1 + + if (inDegrees(inDegreeIndex) == 0) { + queue += inDegreeIndex + } + } + } + + seq.toArray + } + + def topologySortByDFS(): Array[Int] = { + val inverseAdj = new Array[mutable.MutableList[Int]](vertex) + for (i <- Range(0, vertex)) { + inverseAdj(i) = new mutable.MutableList[Int]() + } + + //build the inverse adj + for (i <- Range(0, vertex)) { + for (j <- adjacency(i).indices) { + val index = adjacency(i).get(j).get + inverseAdj(index) += i + } + } + + val visited = new Array[Boolean](vertex) + val seq = new ArrayBuffer[Int]() + for (i <- Range(0, vertex)) { + if (!visited(i)) { + visited(i) = true + //call dfs + seq ++= dfs(i, inverseAdj, visited) + } + } + + seq.toArray + } + + def dfs(index: Int, inverseAdj: Array[mutable.MutableList[Int]], visited: Array[Boolean]): ArrayBuffer[Int] = { + val seq = new ArrayBuffer[Int]() + + for (i <- inverseAdj(index).indices) { + val sourceIndex = inverseAdj(index).get(i).get + if (!visited(sourceIndex)) { + visited(sourceIndex) = true + seq ++= dfs(sourceIndex, inverseAdj, visited) + } + } + seq += index + seq + } +} + + diff --git a/scala/src/test/scala/ch05_array/ArrayDemoSpec.scala b/scala/src/test/scala/ch05_array/ArrayDemoSpec.scala new file mode 100644 index 00000000..98691ba8 --- /dev/null +++ b/scala/src/test/scala/ch05_array/ArrayDemoSpec.scala @@ -0,0 +1,67 @@ +package ch05_array + +import org.scalatest.{FlatSpec, Matchers} + +class ArrayDemoSpec extends FlatSpec with Matchers { + + behavior of "ArrayDemoTest" + + it should "find value after insert" in { + val demo = new ArrayDemo(10) + assert(demo.insert(0, 'a')) + assert(demo.insert(1, 'b')) + assert(demo.insert(2, 'c')) + assert(demo.insert(3, 'd')) + assert(demo.insert(4, 'e')) + demo.print should equal("abcde") + + assert(demo.insert(2, 'x')) + demo.print should equal("abxcde") + } + + it should "not insert value if capacity is full " in { + val demo = new ArrayDemo(10) + for (i <- Range(0, 10)) { + demo.insert(i, (i + 97).toChar) + } + + assert(!demo.insert(1, 'a')) + } + + it should "not insert if index is negative" in { + val demo = new ArrayDemo(10) + assert(!demo.insert(-1, 'a')) + } + + it should "not insert if index is exceed capacity" in { + val demo = new ArrayDemo(10) + assert(!demo.insert(10, 'a')) + assert(!demo.insert(11, 'a')) + } + + it should "not find after delete" in { + val demo = new ArrayDemo(10) + assert(demo.insert(0, 'a')) + assert(demo.insert(1, 'b')) + assert(demo.insert(2, 'c')) + assert(demo.insert(3, 'd')) + assert(demo.insert(4, 'e')) + demo.print should equal("abcde") + + + assert(demo.insert(2, 'x')) + demo.print should equal("abxcde") + + demo.delete(2) should equal('x') + demo.find(2) should not equal ('x') + demo.print should equal("abcde") + } + + it should "not delete for empty array" in { + val demo = new ArrayDemo(10) + assertThrows[IllegalStateException] { + demo.delete(2) should equal('x') + } + } + +} diff --git a/scala/src/test/scala/ch06_linkedlist/NodeTest.scala b/scala/src/test/scala/ch06_linkedlist/NodeTest.scala new file mode 100644 index 00000000..ed87bf0d --- /dev/null +++ b/scala/src/test/scala/ch06_linkedlist/NodeTest.scala @@ -0,0 +1,18 @@ +package ch06_linkedlist + +import org.scalatest.{FlatSpec, Matchers} + +class NodeTest extends FlatSpec with Matchers { + + behavior of "NodeTest" + it should "create node with constructor" in { + val node1 = new Node(1, None) + val node2 = new Node(2, Some(node1)) + val node3 = new Node(2, Some(node2)) + + node1.next should be(None) + node1.next = Some(node3) + node1.next should equal(Some(node3)) + } + +} diff --git a/scala/src/test/scala/ch06_linkedlist/SinglyLinkedListTest.scala b/scala/src/test/scala/ch06_linkedlist/SinglyLinkedListTest.scala new file mode 100644 index 00000000..da5c3ed3 --- /dev/null +++ b/scala/src/test/scala/ch06_linkedlist/SinglyLinkedListTest.scala @@ -0,0 +1,182 @@ +package ch06_linkedlist + +import org.scalatest.{FlatSpec, Matchers} + +class SinglyLinkedListTest extends FlatSpec with Matchers { + + behavior of "SinglyLinkedListTest" + + it should "insertToHead for new values" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertToHead(0) + list.insertToHead(1) + list.insertToHead(2) + list.insertToHead(3) + + list.mkString() should equal("3210") + + } + + it should "insertTail for new values" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertTail(0) + list.insertTail(1) + list.insertTail(2) + list.insertTail(3) + + list.mkString() should equal("0123") + + } + + it should "find by value" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertTail(0) + list.insertTail(1) + list.insertTail(2) + list.insertTail(3) + + list.mkString() should equal("0123") + val node1 = list.findByValue(1).get + node1.next.get.data should equal(2) + } + + it should "insert after node and can find it back" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertTail(0) + list.insertTail(1) + list.insertTail(3) + list.insertTail(4) + + val node1 = list.findByValue(1).get + list.insertAfter(node1, 2) + + list.mkString() should equal("01234") + } + + it should "insert before node and can find it back" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertTail(0) + list.insertTail(1) + list.insertTail(3) + list.insertTail(4) + + val node3 = list.findByValue(3).get + list.insertBefore(node3, 2) + + list.mkString() should equal("01234") + } + + it should "delete desired node" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertTail(0) + list.insertTail(1) + list.insertTail(2) + list.insertTail(3) + + val node1 = list.findByValue(1).get + list.deleteByNode(node1) + + list.mkString() should equal("023") + } + + it should "delete head node " in { + val list: SinglyLinkedList = new SinglyLinkedList() + + list.insertTail(0) + list.insertTail(1) + list.insertTail(2) + list.insertTail(3) + + val node0 = list.findByValue(0).get + list.deleteByNode(node0) + + list.mkString() should equal("123") + } + + it should "inverseLink to head node" in { + val list: SinglyLinkedList = new SinglyLinkedList() + + for (i <- 0 to 10) { + list.insertTail(i) + } + + val node8 = list.findByValue(8) + + val inverseNode = list.inverseLink(node8.get) + + var node = inverseNode + val result = new StringBuilder + while (node.next.nonEmpty) { + result.append(node.data) + node = node.next.get + } + result.append(node.data) + + result.mkString should equal("876543210") + } + + it should "be isPalindrome for 0123210" in { + val list: SinglyLinkedList = new SinglyLinkedList() + for (i <- 0 to 3) { + list.insertTail(i) + } + + for (i <- 2 to 0 by -1) { + list.insertTail(i) + } + + list.mkString() should equal("0123210") + assert(list.isPalindrome()) + } + + it should "be isPalindrome for 01233210" in { + val list: SinglyLinkedList = new SinglyLinkedList() + for (i <- 0 to 3) { + list.insertTail(i) + } + + for (i <- 3 to 0 by -1) { + list.insertTail(i) + } + + list.mkString() should equal("01233210") + assert(list.isPalindrome()) + } + + it should "not be isPalindrome for 012332100" in { + val list: SinglyLinkedList = new SinglyLinkedList() + for (i <- 0 to 3) { + list.insertTail(i) + } + + for (i <- 3 to 0 by -1) { + list.insertTail(i) + } + + list.insertTail(0) + + list.mkString() should equal("012332100") + assert(!list.isPalindrome()) + } + + it should "be isPalindrome for large numbers" in { + val list: SinglyLinkedList = new SinglyLinkedList() + val num = 5000 + for (i <- 0 to num) { + list.insertTail(i) + } + + for (i <- num to 0 by -1) { + list.insertTail(i) + } + + assert(list.isPalindrome()) + } + +} diff --git a/scala/src/test/scala/ch07_linkedlist/LinkedListAlgoTest.scala b/scala/src/test/scala/ch07_linkedlist/LinkedListAlgoTest.scala new file mode 100644 index 00000000..2b72e3f3 --- /dev/null +++ b/scala/src/test/scala/ch07_linkedlist/LinkedListAlgoTest.scala @@ -0,0 +1,254 @@ +package ch07_linkedlist + +import ch06_linkedlist.{Node, SinglyLinkedList} +import org.scalatest.{FlatSpec, Matchers} + +class LinkedListAlgoTest extends FlatSpec with Matchers { + + behavior of "LinkedListAlgoTest" + + it should "reverse a node for long linked list" in { + val list = new SinglyLinkedList() + + for (i <- 0 to 9) { + list.insertTail(i) + } + LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) + + val reverseNode = LinkedListAlgo.reverse(list.headOpt.get) + + LinkedListAlgo.mkStringForChain(reverseNode) should equal((9 to 0 by -1).toArray.mkString("")) + } + + it should "reverse a node for linked list with 1 node" in { + val list = new SinglyLinkedList() + + list.insertTail(100) + + LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal("100") + + val reverseNode = LinkedListAlgo.reverse(list.headOpt.get) + + LinkedListAlgo.mkStringForChain(reverseNode) should equal("100") + } + + it should "check circle for circled linked list" in { + + val node1 = new Node(1, None) + val node2 = new Node(2, None) + val node3 = new Node(3, None) + val node4 = new Node(4, None) + val node5 = new Node(5, None) + val node6 = new Node(6, None) + + //1->2->3->4->5->6->3 it's a circle + //node1 is the head + node1.next = Some(node2) + node2.next = Some(node3) + node3.next = Some(node4) + node4.next = Some(node5) + node5.next = Some(node6) + node6.next = Some(node3) + + assert(LinkedListAlgo.checkCircle(node1).isDefined) + } + + it should "check circle for none circled linked list" in { + + val node1 = new Node(1, None) + val node2 = new Node(2, None) + val node3 = new Node(3, None) + val node4 = new Node(4, None) + val node5 = new Node(5, None) + val node6 = new Node(6, None) + + //1->2->3->4->5->6->null it's not a circle + //node1 is the head + node1.next = Some(node2) + node2.next = Some(node3) + node3.next = Some(node4) + node4.next = Some(node5) + node5.next = Some(node6) + + assert(LinkedListAlgo.checkCircle(node1).isEmpty) + } + + it should "calculate circle length" in { + val node1 = new Node(1, None) + val node2 = new Node(2, None) + val node3 = new Node(3, None) + val node4 = new Node(4, None) + val node5 = new Node(5, None) + val node6 = new Node(6, None) + + //1->2->3->4->5->6->3 it's a circle + //node1 is the head + node1.next = Some(node2) + node2.next = Some(node3) + node3.next = Some(node4) + node4.next = Some(node5) + node5.next = Some(node6) + node6.next = Some(node3) + + val node = LinkedListAlgo.checkCircle(node1).get + + val length = LinkedListAlgo.calculateCircleLength(node) + + length should equal(4) + } + + it should "find entrance of the circle" in { + val node1 = new Node(1, None) + val node2 = new Node(2, None) + val node3 = new Node(3, None) + val node4 = new Node(4, None) + val node5 = new Node(5, None) + val node6 = new Node(6, None) + + //1->2->3->4->5->6->3 it's a circle + //node1 is the head + node1.next = Some(node2) + node2.next = Some(node3) + node3.next = Some(node4) + node4.next = Some(node5) + node5.next = Some(node6) + node6.next = Some(node3) + + val node = LinkedListAlgo.findCircleEntrance(node1) + + node.get.data should equal(3) + } + + it should "merge 2 sorted list into 1" in { + val node1 = new Node(1, None) + val node2 = new Node(2, None) + val node3 = new Node(3, None) + val node4 = new Node(4, None) + val node5 = new Node(5, None) + val node6 = new Node(6, None) + //1->2->3->4->5->6->null + //node1 is the head + node1.next = Some(node2) + node2.next = Some(node3) + node3.next = Some(node4) + node4.next = Some(node5) + node5.next = Some(node6) + + val nodeA = new Node(2, None) + val nodeB = new Node(4, None) + val nodeC = new Node(6, None) + val nodeD = new Node(8, None) + val nodeE = new Node(10, None) + //2->4->6->8->10->null + //nodeA is the head + nodeA.next = Some(nodeB) + nodeB.next = Some(nodeC) + nodeC.next = Some(nodeD) + nodeD.next = Some(nodeE) + + val node = LinkedListAlgo.mergeSortedList(Some(node1), Some(nodeA)) + + assert(node.isDefined) + + LinkedListAlgo.mkStringForChain(node.get) should equal("122344566810") + } + + it should "merge empty list into exist list " in { + val node1 = new Node(1, None) + val node2 = new Node(2, None) + val node3 = new Node(3, None) + val node4 = new Node(4, None) + val node5 = new Node(5, None) + val node6 = new Node(6, None) + //1->2->3->4->5->6->null + //node1 is the head + node1.next = Some(node2) + node2.next = Some(node3) + node3.next = Some(node4) + node4.next = Some(node5) + node5.next = Some(node6) + + val nodeA = LinkedListAlgo.mergeSortedList(Some(node1), None) + assert(nodeA.isDefined) + LinkedListAlgo.mkStringForChain(nodeA.get) should equal("123456") + + val nodeB = LinkedListAlgo.mergeSortedList(None, Some(node1)) + assert(nodeB.isDefined) + LinkedListAlgo.mkStringForChain(nodeB.get) should equal("123456") + } + + it should "merge 2 empty lists " in { + val node = LinkedListAlgo.mergeSortedList(None, None) + assert(node.isEmpty) + } + + it should "delete last 2nd element " in { + val list = new SinglyLinkedList() + + for (i <- 0 to 9) { + list.insertTail(i) + } + LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) + + val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 2) + assert(node.isDefined) + LinkedListAlgo.mkStringForChain(node.get) should equal("012345679") + } + + it should "delete last element " in { + val list = new SinglyLinkedList() + + for (i <- 0 to 9) { + list.insertTail(i) + } + LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) + + val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 1) + assert(node.isDefined) + LinkedListAlgo.mkStringForChain(node.get) should equal("012345678") + } + + it should "delete first element " in { + val list = new SinglyLinkedList() + + for (i <- 0 to 9) { + list.insertTail(i) + } + LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString("")) + + val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 10) + assert(node.isDefined) + LinkedListAlgo.mkStringForChain(node.get) should equal("123456789") + } + + it should "delete firs only element " in { + val list = new SinglyLinkedList() + list.insertTail(0) + + val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 1) + assert(node.isEmpty) + } + + it should "throw exception if k < 0 " in { + val list = new SinglyLinkedList() + + for (i <- 0 to 9) { + list.insertTail(i) + } + assertThrows[IllegalArgumentException] { + LinkedListAlgo.deleteLastKthNode(list.headOpt, -1) + } + } + + it should "throw exception if k greater than list length " in { + val list = new SinglyLinkedList() + + for (i <- 0 to 9) { + list.insertTail(i) + } + assertThrows[IllegalArgumentException] { + LinkedListAlgo.deleteLastKthNode(list.headOpt, 15) + } + } + +} diff --git a/scala/src/test/scala/ch08_stack/BrowserDemoTest.scala b/scala/src/test/scala/ch08_stack/BrowserDemoTest.scala new file mode 100644 index 00000000..e78665cf --- /dev/null +++ b/scala/src/test/scala/ch08_stack/BrowserDemoTest.scala @@ -0,0 +1,74 @@ +package ch08_stack + +import org.scalatest.{FlatSpec, Matchers} + +class BrowserDemoTest extends FlatSpec with Matchers { + + behavior of "BrowserDemoTest" + + it should "canGoBack" in { + val browser = new BrowserDemo() + assert(!browser.canGoBack()) + browser.open("a") + assert(!browser.canGoBack()) + browser.open("b") + assert(browser.canGoBack()) + browser.currentPageOpt.get should equal("b") + } + + it should "goBack" in { + val browser = new BrowserDemo() + browser.open("a") + browser.open("b") + browser.currentPageOpt.get should equal("b") + assert(browser.canGoBack()) + browser.goBack() + assert(!browser.canGoBack()) + browser.currentPageOpt.get should equal("a") + } + + it should "canGoForward" in { + val browser = new BrowserDemo() + browser.open("a") + browser.open("b") + browser.currentPageOpt.get should equal("b") + assert(browser.canGoBack()) + browser.goBack() + assert(!browser.canGoBack()) + browser.currentPageOpt.get should equal("a") + assert(browser.canGoForward()) + } + + it should "goForward" in { + val browser = new BrowserDemo() + browser.open("a") + browser.open("b") + browser.currentPageOpt.get should equal("b") + assert(browser.canGoBack()) + browser.goBack() + assert(!browser.canGoBack()) + browser.currentPageOpt.get should equal("a") + assert(browser.canGoForward()) + browser.goForward() + browser.currentPageOpt.get should equal("b") + } + + it should "open new page to clear forward stack" in { + val browser = new BrowserDemo() + browser.open("a") + browser.open("b") + browser.currentPageOpt.get should equal("b") + assert(browser.canGoBack()) + browser.goBack() + assert(!browser.canGoBack()) + browser.currentPageOpt.get should equal("a") + assert(browser.canGoForward()) + browser.open("c") + assert(!browser.canGoForward()) + browser.goBack() + browser.currentPageOpt.get should equal("a") + } + + + +} diff --git a/scala/src/test/scala/ch08_stack/StackDemoTest.scala b/scala/src/test/scala/ch08_stack/StackDemoTest.scala new file mode 100644 index 00000000..a875f9eb --- /dev/null +++ b/scala/src/test/scala/ch08_stack/StackDemoTest.scala @@ -0,0 +1,29 @@ +package ch08_stack + +import org.scalatest.{FlatSpec, Matchers} + +class StackDemoTest extends FlatSpec with Matchers { + + behavior of "StackDemoTest" + + it should "push/pop should be FILO" in { + val stack = new StackDemo[String] + val num = 100 + for (i <- 1 to num) { + stack.push(i.toString) + } + + for (i <- num to 1 by -1) { + stack.pop().get.data should equal(i.toString) + } + } + + it should "pop should also work for empty stack" in { + val stack = new StackDemo[Int] + val num = 100 + for (i <- num to 1 by -1) { + assert(stack.pop().isEmpty) + } + } + +} diff --git a/scala/src/test/scala/ch09_queue/ArrayQueueTest.scala b/scala/src/test/scala/ch09_queue/ArrayQueueTest.scala new file mode 100644 index 00000000..33b79907 --- /dev/null +++ b/scala/src/test/scala/ch09_queue/ArrayQueueTest.scala @@ -0,0 +1,6 @@ +package ch09_queue + +class ArrayQueueTest extends DemoQueueTest { + + override def getInstance() = new ArrayQueue[Int](15) +} diff --git a/scala/src/test/scala/ch09_queue/CircularQueueTest.scala b/scala/src/test/scala/ch09_queue/CircularQueueTest.scala new file mode 100644 index 00000000..a8ea3945 --- /dev/null +++ b/scala/src/test/scala/ch09_queue/CircularQueueTest.scala @@ -0,0 +1,6 @@ +package ch09_queue + +class CircularQueueTest extends DemoQueueTest { + + override def getInstance(): DemoQueue[Int] = new CircularQueue[Int](15) +} diff --git a/scala/src/test/scala/ch09_queue/DemoQueueTest.scala b/scala/src/test/scala/ch09_queue/DemoQueueTest.scala new file mode 100644 index 00000000..ef0db234 --- /dev/null +++ b/scala/src/test/scala/ch09_queue/DemoQueueTest.scala @@ -0,0 +1,28 @@ +package ch09_queue + +import org.scalatest.{FlatSpec, Matchers} + +abstract class DemoQueueTest extends FlatSpec with Matchers { + + def getInstance(): DemoQueue[Int] + + behavior of "test" + + it should "dequeue nothing for empty queue" in { + val queue = getInstance() + assert(queue.dequeue().isEmpty) + } + + it should "enqueue/dequeue should be FIFO" in { + val queue = getInstance() + for (i <- Range(0, 10)) { + queue.enqueue(i) + } + + queue.size should equal(10) + + for (i <- Range(0, 10)) { + queue.dequeue().get should equal(i) + } + } +} diff --git a/scala/src/test/scala/ch09_queue/DynamicArrayQueueTest.scala b/scala/src/test/scala/ch09_queue/DynamicArrayQueueTest.scala new file mode 100644 index 00000000..46c14030 --- /dev/null +++ b/scala/src/test/scala/ch09_queue/DynamicArrayQueueTest.scala @@ -0,0 +1,20 @@ +package ch09_queue + +class DynamicArrayQueueTest extends DemoQueueTest { + + override def getInstance(): DemoQueue[Int] = new DynamicArrayQueue[Int](15) + + it should "copy data when tail reach the end of the queue" in { + val queue = getInstance() + for (i <- Range(0, 15)) { + queue.enqueue(i) + } + queue.size should equal(15) + queue.dequeue().get should equal(0) + + //enqueue another one + queue.enqueue(30) + queue.size should equal(15) + + } +} diff --git a/scala/src/test/scala/ch09_queue/LinkedListQueueTest.scala b/scala/src/test/scala/ch09_queue/LinkedListQueueTest.scala new file mode 100644 index 00000000..1ce30767 --- /dev/null +++ b/scala/src/test/scala/ch09_queue/LinkedListQueueTest.scala @@ -0,0 +1,7 @@ +package ch09_queue + +class LinkedListQueueTest extends DemoQueueTest { + + override def getInstance() = new LinkListQueue[Int] + +} diff --git a/scala/src/test/scala/ch10_recursive/RecursiveDemoTest.scala b/scala/src/test/scala/ch10_recursive/RecursiveDemoTest.scala new file mode 100644 index 00000000..1987ecea --- /dev/null +++ b/scala/src/test/scala/ch10_recursive/RecursiveDemoTest.scala @@ -0,0 +1,17 @@ +package ch10_recursive + +import org.scalatest.{FlatSpec, Matchers} + +class RecursiveDemoTest extends FlatSpec with Matchers { + + behavior of "RecursiveDemoTest" + + it should "calculateStepWays" in { + RecursiveDemo.calculateStepWays(1) should equal(1) + RecursiveDemo.calculateStepWays(2) should equal(2) + RecursiveDemo.calculateStepWays(3) should equal(3) + RecursiveDemo.calculateStepWays(4) should equal(5) + RecursiveDemo.calculateStepWays(5) should equal(8) + } + +} diff --git a/scala/src/test/scala/ch11_sorts/SortsTest.scala b/scala/src/test/scala/ch11_sorts/SortsTest.scala new file mode 100644 index 00000000..ed5eeb1a --- /dev/null +++ b/scala/src/test/scala/ch11_sorts/SortsTest.scala @@ -0,0 +1,66 @@ +package ch11_sorts + +import ch12_sorts.{MergeSort, QuickSort} +import ch28_heap.Heap +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class SortsTest extends FlatSpec with Matchers { + + behavior of "SortsTest in ch11" + + it should "bubbleSort int arrays" in { + var array = Array(4, 5, 6, 3, 2, 1) + array = Sorts.bubbleSort(array) + array.mkString("") should equal("123456") + + array = Array(4) + array = Sorts.bubbleSort(array) + array.mkString("") should equal("4") + } + + it should "insertSort int arrays" in { + var array = Array(4, 5, 6, 1, 3, 2) + array = Sorts.insertSort(array) + array.mkString("") should equal("123456") + + array = Array(4) + array = Sorts.insertSort(array) + array.mkString("") should equal("4") + } + + it should "selectionSort int arrays" in { + var array = Array(4, 5, 6, 1, 3, 2) + array = Sorts.insertSort(array) + array.mkString("") should equal("123456") + + array = Array(4) + array = Sorts.insertSort(array) + array.mkString("") should equal("4") + } + + + it should "compare the sort algo" in { + val length = 50000 + val array = new Array[Int](length) + val rnd = new Random() + for (i <- Range(0, length)) { + array(i) = rnd.nextInt() + } + + timed("bubbleSort", Sorts.bubbleSort, array.clone()) + timed("insertSort", Sorts.insertSort, array.clone()) + timed("selectionSort", Sorts.selectionSort, array.clone()) + timed("mergeSort", MergeSort.mergeSort, array.clone()) + timed("quickSort", QuickSort.quickSort, array.clone()) + timed("heapSort", Heap.heapSort, array.clone()) + } + + def reportElapsed(name: String, time: Long): Unit = println(name + " takes in " + time + "ms") + + def timed(name: String, f: (Array[Int]) => Unit, array: Array[Int]): Unit = { + val start = System.currentTimeMillis() + try f(array) finally reportElapsed(name, System.currentTimeMillis - start) + } +} diff --git a/scala/src/test/scala/ch12_sorts/MergeSortTest.scala b/scala/src/test/scala/ch12_sorts/MergeSortTest.scala new file mode 100644 index 00000000..6944aaca --- /dev/null +++ b/scala/src/test/scala/ch12_sorts/MergeSortTest.scala @@ -0,0 +1,22 @@ +package ch12_sorts + + +import org.scalatest.{FlatSpec, Matchers} + +class MergeSortTest extends FlatSpec with Matchers { + behavior of "SortsTest in ch12" + + it should "mergeSort int arrays" in { + var array = Array(4, 5, 6, 3, 2, 1) + array = MergeSort.mergeSort(array) + array.mkString("") should equal("123456") + + array = Array(4) + array = MergeSort.mergeSort(array) + array.mkString("") should equal("4") + + array = Array(4, 2) + array = MergeSort.mergeSort(array) + array.mkString("") should equal("24") + } +} diff --git a/scala/src/test/scala/ch12_sorts/QuickSortTest.scala b/scala/src/test/scala/ch12_sorts/QuickSortTest.scala new file mode 100644 index 00000000..cafeedbb --- /dev/null +++ b/scala/src/test/scala/ch12_sorts/QuickSortTest.scala @@ -0,0 +1,31 @@ +package ch12_sorts + +import org.scalatest.{FlatSpec, Matchers} + +class QuickSortTest extends FlatSpec with Matchers { + + behavior of "QuickSortTest" + + it should "quickSort" in { + var array = Array(4, 5, 6, 3, 2, 1) + array = QuickSort.quickSort(array) + array.mkString("") should equal("123456") + + array = Array(4) + array = QuickSort.quickSort(array) + array.mkString("") should equal("4") + + array = Array(4, 2) + array = QuickSort.quickSort(array) + array.mkString("") should equal("24") + } + + it should "find the Kth element in the array" in { + val array = Array(4, 2, 5, 12, 3) + + QuickSort.findKthElement(array, 3) should equal(4) + QuickSort.findKthElement(array, 5) should equal(12) + QuickSort.findKthElement(array, 1) should equal(2) + } + +} diff --git a/scala/src/test/scala/ch15_bsearch/BSearchRecursiveTest.scala b/scala/src/test/scala/ch15_bsearch/BSearchRecursiveTest.scala new file mode 100644 index 00000000..0aa1702b --- /dev/null +++ b/scala/src/test/scala/ch15_bsearch/BSearchRecursiveTest.scala @@ -0,0 +1,25 @@ +package ch15_bsearch + +import ch12_sorts.QuickSort +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class BSearchRecursiveTest extends FlatSpec with Matchers { + + behavior of "BSearchRecursiveTest" + + it should "search with exist value" in { + val length = 50000 + val array = new Array[Int](length) + val rnd = new Random() + for (i <- Range(0, length)) { + array(i) = rnd.nextInt() + } + + val target = array(2698) + + BSearchRecursive.search(QuickSort.quickSort(array), target) should be > -1 + } + +} diff --git a/scala/src/test/scala/ch15_bsearch/BSearchTest.scala b/scala/src/test/scala/ch15_bsearch/BSearchTest.scala new file mode 100644 index 00000000..437e96a9 --- /dev/null +++ b/scala/src/test/scala/ch15_bsearch/BSearchTest.scala @@ -0,0 +1,37 @@ +package ch15_bsearch + +import ch12_sorts.QuickSort +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class BSearchTest extends FlatSpec with Matchers { + + behavior of "BSearchTest" + + it should "search with exist value" in { + val length = 50000 + val array = new Array[Int](length) + val rnd = new Random() + for (i <- Range(0, length)) { + array(i) = rnd.nextInt() + } + + val target = array(2698) + + BSearch.search(QuickSort.quickSort(array), target) should be > -1 + } + + it should "calculate sqrt value -1 " in { + val x = 4 + val precision = 0.000001 + BSearch.sqrt(x, precision) should equal(2.0) + } + + it should "calculate sqrt value -2 " in { + val x = 0.04 + val precision = 0.000001 + BSearch.sqrt(x, precision) should equal(0.2 +- precision) + } + +} diff --git a/scala/src/test/scala/ch16_bsearch/BSearchTest.scala b/scala/src/test/scala/ch16_bsearch/BSearchTest.scala new file mode 100644 index 00000000..89da6fa5 --- /dev/null +++ b/scala/src/test/scala/ch16_bsearch/BSearchTest.scala @@ -0,0 +1,31 @@ +package ch16_bsearch + +import org.scalatest.{FlatSpec, Matchers} + +class BSearchTest extends FlatSpec with Matchers { + + behavior of "BSearchTest" + + it should "findFirstValue" in { + val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) + BSearch.findFirstValue(items, 8) should equal(5) + } + + it should "findLastValue" in { + val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) + BSearch.findLastValue(items, 8) should equal(7) + } + + it should "findFirstGreaterThan" in { + val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) + BSearch.findFirstGreaterThan(items, 2) should equal(1) + BSearch.findFirstGreaterThan(items, 8) should equal(5) + } + + it should "findLastSmallerThan" in { + val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18) + BSearch.findLastSmallerThan(items, 2) should equal(0) + BSearch.findLastSmallerThan(items, 8) should equal(7) + } + +} diff --git a/scala/src/test/scala/ch17_skip_list/SkipListTest.scala b/scala/src/test/scala/ch17_skip_list/SkipListTest.scala new file mode 100644 index 00000000..0b183e64 --- /dev/null +++ b/scala/src/test/scala/ch17_skip_list/SkipListTest.scala @@ -0,0 +1,44 @@ +package ch17_skip_list + +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class SkipListTest extends FlatSpec with Matchers { + + behavior of "SkipListTest" + + it should "insert skip list" in { + val list = new SkipList() + for (i <- Range(0, 10)) { + list.insert(i) + } + + list.mkString() should equal("0123456789") + } + + it should "delete skip list" in { + val list = new SkipList() + for (i <- Range(0, 10)) { + list.insert(i) + } + + list.delete(5) + list.mkString() should equal("012346789") + } + + it should "find value in skip list" in { + val list = new SkipList() + val length = 5000 + val array = new Array[Int](length) + val rnd = new Random() + for (i <- Range(0, length)) { + array(i) = rnd.nextInt(length) + list.insert(array(i)) + } + + assert(list.find(array(rnd.nextInt(length - 1))).isDefined) + assert(list.find(array(rnd.nextInt(length - 1)) + length + 1).isEmpty) + + } +} diff --git a/scala/src/test/scala/ch20_linked_hash_map/LRUCacheTest.scala b/scala/src/test/scala/ch20_linked_hash_map/LRUCacheTest.scala new file mode 100644 index 00000000..26591ad1 --- /dev/null +++ b/scala/src/test/scala/ch20_linked_hash_map/LRUCacheTest.scala @@ -0,0 +1,22 @@ +package ch20_linked_hash_map + +import org.scalatest.{FlatSpec, Matchers} + +class LRUCacheTest extends FlatSpec with Matchers { + + behavior of "LRUCacheTest" + + it should "put data and get back" in { + val cache = new LRUCache[Int, Int](2) + cache.put(1, 1) + cache.put(2, 2) + cache.get(1) should equal(Some(1)) // returns 1 + cache.put(3, 3) // evicts key 2 + cache.get(2) should equal(None) //should not find + cache.put(4, 4) // evicts key 1 + cache.get(1) should equal(None) //should not find + cache.get(3) should equal(Some(3)) // returns 3 + cache.get(4) should equal(Some(4)) // returns 4 + } + +} diff --git a/scala/src/test/scala/ch23_binary_tree/BinaryTreeTest.scala b/scala/src/test/scala/ch23_binary_tree/BinaryTreeTest.scala new file mode 100644 index 00000000..0767f845 --- /dev/null +++ b/scala/src/test/scala/ch23_binary_tree/BinaryTreeTest.scala @@ -0,0 +1,50 @@ +package ch23_binary_tree + +import org.scalatest.{BeforeAndAfterEach, FlatSpec, Matchers} + +class BinaryTreeTest extends FlatSpec with BeforeAndAfterEach with Matchers { + + /* + * A + * / \ + * B C + * / \ / \ + * D E F G + * + */ + var root: Option[Node[String]] = None + val tree = new BinaryTree[String] + + override def beforeEach() { + val D = new Node[String]("D", None, None) + val E = new Node[String]("E", None, None) + val B = new Node[String]("B", Some(D), Some(E)) + val F = new Node[String]("F", None, None) + val G = new Node[String]("G", None, None) + val C = new Node[String]("C", Some(F), Some(G)) + val A = new Node[String]("A", Some(B), Some(C)) + root = Some(A) + } + + override protected def afterEach(): Unit = { + root = None + } + + behavior of "BinaryTreeTest" + + it should "postOrder" in { + tree.postOrder(root) should equal("DEBFGCA") + } + + it should "inOrder" in { + tree.inOrder(root) should equal("DBEAFCG") + } + + it should "preOrder" in { + tree.preOrder(root) should equal("ABDECFG") + } + + it should "levelOrder" in { + tree.levelOrder(root) should equal("ABCDEFG") + } +} diff --git a/scala/src/test/scala/ch24_binary_search_tree/BinarySearchTreeTest.scala b/scala/src/test/scala/ch24_binary_search_tree/BinarySearchTreeTest.scala new file mode 100644 index 00000000..771cbe40 --- /dev/null +++ b/scala/src/test/scala/ch24_binary_search_tree/BinarySearchTreeTest.scala @@ -0,0 +1,76 @@ +package ch24_binary_search_tree + +import org.scalatest.{FlatSpec, Matchers} + +class BinarySearchTreeTest extends FlatSpec with Matchers { + + /* + * 33 + * / \ + * 17 50 + * / \ / \ + * 13 18 34 58 + * \ \ / \ + * 16 25 51 66 + * / \ \ + * 19 27 55 + * + */ + behavior of "BinarySearchTreeTest" + + it should "insert" in { + val tree = new BinarySearchTree(None) + val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) + nums.foreach(tree.insert) + tree.inOrder(tree.root) should equal(nums.sorted.mkString("")) + tree.preOrder(tree.root) should equal("3317131618251927503458515566") + tree.postOrder(tree.root) should equal("1613192725181734555166585033") + tree.levelOrder(tree.root) should equal("3317501318345816255166192755") + } + + it should "find " in { + val tree = new BinarySearchTree(None) + val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) + nums.foreach(tree.insert) + + nums.foreach(num => { + assert(tree.find(num).isDefined) + tree.find(num).get.data should equal(num) + }) + assert(tree.find(100).isEmpty) + } + + it should "delete" in { + val tree = new BinarySearchTree(None) + val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) + nums.foreach(tree.insert) + tree.delete(13) + tree.inOrder(tree.root) should equal(nums.sorted.tail.mkString("")) + tree.delete(18) + tree.inOrder(tree.root) should equal("1617" + nums.sorted.slice(4, nums.size).mkString("")) + tree.delete(66) + tree.inOrder(tree.root) should equal("1617" + nums.sorted.slice(4, nums.size - 1).mkString("")) + } + + it should "calc height of a tree -1" in { + val tree = new BinarySearchTree(None) + val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55) + nums.foreach(tree.insert) + tree.height() should equal(5) + } + + it should "calc height of a tree -2" in { + val tree = new BinarySearchTree(None) + val nums = Array(33, 17, 50, 13, 18, 34, 88).sorted + nums.foreach(tree.insert) + tree.height() should equal(7) + } + + it should "calc height of a tree -3" in { + val tree = new BinarySearchTree(None) + val nums = Array(33).sorted + nums.foreach(tree.insert) + tree.height() should equal(1) + } + +} diff --git a/scala/src/test/scala/ch28_heap/HeapTest.scala b/scala/src/test/scala/ch28_heap/HeapTest.scala new file mode 100644 index 00000000..3e9051ce --- /dev/null +++ b/scala/src/test/scala/ch28_heap/HeapTest.scala @@ -0,0 +1,41 @@ +package ch28_heap + +import org.scalatest.{FlatSpec, Matchers} + +class HeapTest extends FlatSpec with Matchers { + + behavior of "HeapTest" + + it should "insert and removeMax" in { + val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) + val heap = new Heap(nums.length + 1) + nums.foreach(heap.insert) + + heap.removeMax() should equal(33) + heap.removeMax() should equal(27) + } + + it should "build heap from array - bottom up" in { + val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) + val heap = new Heap(nums, true) + + heap.removeMax() should equal(33) + heap.removeMax() should equal(27) + } + + it should "build heap from array - top down" in { + val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) + val heap = new Heap(nums, false) + + heap.removeMax() should equal(33) + heap.removeMax() should equal(27) + } + + it should "sort array" in { + val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12) + val result = Heap.heapSort(nums) + + result.mkString("") should equal(nums.sorted.mkString("")) + } + +} diff --git a/scala/src/test/scala/ch29_heap_solutions/FileMergerTest.scala b/scala/src/test/scala/ch29_heap_solutions/FileMergerTest.scala new file mode 100644 index 00000000..b5de3f02 --- /dev/null +++ b/scala/src/test/scala/ch29_heap_solutions/FileMergerTest.scala @@ -0,0 +1,53 @@ +package ch29_heap_solutions + +import java.io.{BufferedWriter, File, FileWriter} + +import org.scalatest.{FlatSpec, Matchers} + +import scala.io.Source +import scala.util.Random + +class FileMergerTest extends FlatSpec with Matchers { + + behavior of "FileMergerTest" + + it should "mergeFiles" in { + val num = 10 + val contentCount = 10 + val random = Random.alphanumeric + val files = new Array[File](num) + for (i <- Range(0, num)) { + val file = File.createTempFile(i + "-small", ".txt") + files(i) = file + val writer = new BufferedWriter(new FileWriter(file)) + val content = random.take((i + 1) * contentCount).toArray.slice(i * contentCount, (i + 1) * contentCount) + + writer.write(content.sorted) + writer.flush() + writer.close() + } + println("small files below") + files.foreach(printFile) + + val mergedFile = FileMerger.mergeFiles(files.toList) + + val raw = Source.fromFile(mergedFile).toArray + raw should equal(raw.sorted) + raw.length should equal(num * contentCount) + + println("") + println("merged file below") + printFile(mergedFile) + + //clean up + files.foreach(_.delete()) + mergedFile.delete() + + } + + def printFile(file: File): Unit = { + val source = Source.fromFile(file) + source.getLines().foreach(println) + } + +} diff --git a/scala/src/test/scala/ch29_heap_solutions/MiddleNumberKeeperTest.scala b/scala/src/test/scala/ch29_heap_solutions/MiddleNumberKeeperTest.scala new file mode 100644 index 00000000..2dd74f94 --- /dev/null +++ b/scala/src/test/scala/ch29_heap_solutions/MiddleNumberKeeperTest.scala @@ -0,0 +1,28 @@ +package ch29_heap_solutions + +import org.scalatest.{FlatSpec, Matchers} + +class MiddleNumberKeeperTest extends FlatSpec with Matchers { + + behavior of "MiddleNumberKeeperTest" + + it should "get middle of the array" in { + val numKeeper = new MiddleNumberKeeper() + for (i <- Range(0, 10)) { + numKeeper.put(i) + } + + numKeeper.get().get should equal(4) + } + + it should "get 90% position of the array" in { + val numKeeper = new MiddleNumberKeeper(0.9) + for (i <- Range(0, 9)) { + numKeeper.put(i) + } + numKeeper.put(9) + + numKeeper.get().get should equal(8) + } + +} diff --git a/scala/src/test/scala/ch29_heap_solutions/TopKItemsKeeperTest.scala b/scala/src/test/scala/ch29_heap_solutions/TopKItemsKeeperTest.scala new file mode 100644 index 00000000..b3dd0c9d --- /dev/null +++ b/scala/src/test/scala/ch29_heap_solutions/TopKItemsKeeperTest.scala @@ -0,0 +1,25 @@ +package ch29_heap_solutions + +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class TopKItemsKeeperTest extends FlatSpec with Matchers { + + behavior of "TopKItemsKeeperTest" + + it should "put and get top K from the keeper" in { + val length = 50 + val k = 5 + val topKItemsKeeper = new TopKItemsKeeper(k) + val nums = new Array[Int](length) + for (i <- Range(0, length)) { + nums(i) = Random.nextInt + } + + nums.foreach(topKItemsKeeper.put) + val ordering = scala.math.Ordering.Int.reverse + topKItemsKeeper.get().toArray.sorted(ordering) should equal(nums.sorted(ordering).slice(0, k)) + } + +} diff --git a/scala/src/test/scala/ch31_graph/GraphTest.scala b/scala/src/test/scala/ch31_graph/GraphTest.scala new file mode 100644 index 00000000..7dfc7578 --- /dev/null +++ b/scala/src/test/scala/ch31_graph/GraphTest.scala @@ -0,0 +1,64 @@ +package ch31_graph + +import org.scalatest.{FlatSpec, Matchers} + +class GraphTest extends FlatSpec with Matchers { + + /* + 0 - 1 - 2 + | | | + 3 - 4 - 5 + | | + 6 - 7 + */ + + behavior of "GraphTest" + + def initGraph: Graph = { + val num = 8 + val vertex = new Array[String](num) + for (i <- Range(0, num)) { + vertex(i) = i.toString + } + val graph = new Graph(vertex) + + graph.addEdge("0", "1") + graph.addEdge("1", "2") + graph.addEdge("0", "3") + graph.addEdge("1", "4") + graph.addEdge("2", "5") + graph.addEdge("3", "4") + graph.addEdge("4", "5") + graph.addEdge("4", "6") + graph.addEdge("5", "7") + graph.addEdge("6", "7") + graph + } + + it should "construct the graph" in { + val graph: Graph = initGraph + + graph.getEdges("0").sorted should equal(Array("1", "3")) + graph.getEdges("1").sorted should equal(Array("0", "2", "4")) + graph.getEdges("2").sorted should equal(Array("1", "5")) + graph.getEdges("3").sorted should equal(Array("0", "4")) + graph.getEdges("4").sorted should equal(Array("1", "3", "5", "6")) + graph.getEdges("5").sorted should equal(Array("2", "4", "7")) + graph.getEdges("6").sorted should equal(Array("4", "7")) + graph.getEdges("7").sorted should equal(Array("5", "6")) + } + + it should "do breath first search in graph" in { + val graph: Graph = initGraph + graph.breathFirstSearch("0", "4").get should equal(Array("0", "1", "3", "2", "4")) + graph.breathFirstSearch("1", "5").get should equal(Array("1", "0", "2", "4", "3", "5")) + assert(graph.breathFirstSearch("1", "8").isEmpty) + } + + it should "do depth first search in graph" in { + val graph: Graph = initGraph + graph.depthFirstSearch("0", "4").get should equal(Array("0", "1", "2", "5", "4")) + graph.depthFirstSearch("1", "5").get should equal(Array("1", "0", "3", "4", "5")) + assert(graph.depthFirstSearch("1", "8").isEmpty) + } +} diff --git a/scala/src/test/scala/ch32_matching/BruteForceTest.scala b/scala/src/test/scala/ch32_matching/BruteForceTest.scala new file mode 100644 index 00000000..44f8700b --- /dev/null +++ b/scala/src/test/scala/ch32_matching/BruteForceTest.scala @@ -0,0 +1,20 @@ +package ch32_matching + +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class BruteForceTest extends FlatSpec with Matchers { + + behavior of "BruteForceTest" + + it should "find firstIndexOf a sub string" in { + val random = Random.alphanumeric + val main = random.take(1000).toArray + val index = Random.nextInt(950) + val sub = random.take(1000).toArray.slice(index, index + 50) + + BruteForce.firstIndexOf(main, sub) should equal(index) + } + +} diff --git a/scala/src/test/scala/ch32_matching/RabinKarpTest.scala b/scala/src/test/scala/ch32_matching/RabinKarpTest.scala new file mode 100644 index 00000000..f766f8a4 --- /dev/null +++ b/scala/src/test/scala/ch32_matching/RabinKarpTest.scala @@ -0,0 +1,17 @@ +package ch32_matching + +import org.scalatest.{FlatSpec, Matchers} + +import scala.util.Random + +class RabinKarpTest extends FlatSpec with Matchers { + + it should "find firstIndexOf a sub string" in { + val random = Random.alphanumeric + val main = random.take(1000).toArray + val index = Random.nextInt(950) + val sub = random.take(1000).toArray.slice(index, index + 50) + + RabinKarp.firstIndexOf(main, sub) should equal(index) + } +} diff --git a/scala/src/test/scala/ch35_tire_tree/TrieTreeTest.scala b/scala/src/test/scala/ch35_tire_tree/TrieTreeTest.scala new file mode 100644 index 00000000..d6ebfa91 --- /dev/null +++ b/scala/src/test/scala/ch35_tire_tree/TrieTreeTest.scala @@ -0,0 +1,48 @@ +package ch35_tire_tree + +import org.scalatest.{FlatSpec, Matchers} + +class TrieTreeTest extends FlatSpec with Matchers { + + behavior of "TrieTreeTest" + + it should "insert data into the tree and find it back" in { + val texts = Array("how", "hi", "her", "hello", "so", "see") + val tree = new TrieTree() + for (i <- texts.indices) { + tree.insert(texts(i).toCharArray) + } + + for (i <- texts.indices) { + assert(tree.find(texts(i).toCharArray)) + } + } + + it should "find prefix" in { + val texts = Array("how", "hi", "her", "hello", "so", "see") + val tree = new TrieTree() + for (i <- texts.indices) { + tree.insert(texts(i).toCharArray) + } + + for (i <- texts.indices) { + assert(tree.prefix(texts(i).toCharArray.slice(0, 1))) + } + + assert(!tree.prefix("howlo".toCharArray)) + assert(!tree.prefix("aaa".toCharArray)) + } + + it should "give suggestion" in { + val texts = Array("how", "hi", "her", "hello", "so", "see") + val tree = new TrieTree() + for (i <- texts.indices) { + tree.insert(texts(i).toCharArray) + } + + tree.suggestion("he".toCharArray).get should contain("her") + tree.suggestion("he".toCharArray).get should contain("hello") + tree.suggestion("he".toCharArray).get.size should equal(2) + } + +} diff --git a/scala/src/test/scala/ch39_back_tracking/BagWeightTest.scala b/scala/src/test/scala/ch39_back_tracking/BagWeightTest.scala new file mode 100644 index 00000000..870754bb --- /dev/null +++ b/scala/src/test/scala/ch39_back_tracking/BagWeightTest.scala @@ -0,0 +1,15 @@ +package ch39_back_tracking + +import org.scalatest.FlatSpec + +class BagWeightTest extends FlatSpec { + + behavior of "BagWeightTest" + + it should "calculateMaxWeight" in { + val bagWeight = new BagWeight(5,9) + val maxWeight = bagWeight.calculateMaxWeight(Array(1,2,3,5,6)) + println(maxWeight) + } + +} diff --git a/scala/src/test/scala/ch39_back_tracking/EightQueensTest.scala b/scala/src/test/scala/ch39_back_tracking/EightQueensTest.scala new file mode 100644 index 00000000..03d6e7a8 --- /dev/null +++ b/scala/src/test/scala/ch39_back_tracking/EightQueensTest.scala @@ -0,0 +1,14 @@ +package ch39_back_tracking + +import org.scalatest.FlatSpec + +class EightQueensTest extends FlatSpec { + + behavior of "EightQueensTest" + + it should "calc8Queues" in { + val eightQueens = new EightQueens() + eightQueens.calc8Queues(0) + } + +} diff --git a/scala/src/test/scala/ch39_back_tracking/NQueensTest.scala b/scala/src/test/scala/ch39_back_tracking/NQueensTest.scala new file mode 100644 index 00000000..e4e91ca3 --- /dev/null +++ b/scala/src/test/scala/ch39_back_tracking/NQueensTest.scala @@ -0,0 +1,21 @@ +package ch39_back_tracking + +import org.scalatest.FlatSpec + +class NQueensTest extends FlatSpec { + + behavior of "NQueensTest" + + it should "calc8Queues" in { + val eightQueens = new NQueens(8) + eightQueens.calcNQueues(0) + + } + + it should "calc4Queues" in { + val eightQueens = new NQueens(4) + eightQueens.calcNQueues(0) + + } + +} diff --git a/scala/src/test/scala/ch39_back_tracking/SudokuTest.scala b/scala/src/test/scala/ch39_back_tracking/SudokuTest.scala new file mode 100644 index 00000000..878c613e --- /dev/null +++ b/scala/src/test/scala/ch39_back_tracking/SudokuTest.scala @@ -0,0 +1,74 @@ +package ch39_back_tracking + +import org.scalatest.FlatSpec + +class SudokuTest extends FlatSpec { + + behavior of "SudokuTest" + + it should "resolve - 1" in { + val grid = Array( + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0) + ) + val sudoku = new Sudoku() + sudoku.resolve(grid) + } + + it should "resolve - 2" in { + val grid = Array( + Array(0, 0, 7, 2, 5, 6, 4, 0 ,0), + Array(4, 0, 0, 0, 0, 0, 0, 0, 5), + Array(0, 1, 0, 0, 3, 9, 0, 6, 0), + Array(0, 0, 0, 5, 0, 8, 0, 0, 0), + Array(0, 0, 8, 0, 6, 0, 2, 0, 0), + Array(0, 9, 0, 1, 0, 7, 0, 0, 0), + Array(0, 3, 0, 0, 7, 0, 0, 9, 0), + Array(2, 0, 0, 0, 0, 0, 0, 0, 4), + Array(0, 0, 6, 3, 1, 2, 7, 0, 8) + ) + val sudoku = new Sudoku() + sudoku.resolve(grid) + } + + it should "resolve - 3" in { + val grid = Array( + Array(3, 8, 0, 0, 0, 0, 0, 0, 1), + Array(0, 0, 6, 4, 0, 0, 7, 8, 5), + Array(0, 0, 9, 0, 2, 0, 3, 0, 0), + Array(0, 6, 0, 0, 9, 0, 0, 0, 0), + Array(8, 0, 0, 3, 0, 2, 0, 0, 9), + Array(0, 0, 0, 0, 4, 0, 0, 7, 0), + Array(0, 0, 1, 0, 7, 0, 5, 0, 0), + Array(4, 9, 5, 0, 0, 6, 1, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 9, 2) + ) + val sudoku = new Sudoku() + sudoku.resolve(grid) + } + + it should "print grid" in { + val grid = Array( + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0), + Array(0, 0, 0, 0, 0, 0, 0, 0, 0) + ) + + val sudoku = new Sudoku() + sudoku.printGrid(grid) + } + +} diff --git a/scala/src/test/scala/ch43_topology_sort/GraphTopologyTest.scala b/scala/src/test/scala/ch43_topology_sort/GraphTopologyTest.scala new file mode 100644 index 00000000..dfb24328 --- /dev/null +++ b/scala/src/test/scala/ch43_topology_sort/GraphTopologyTest.scala @@ -0,0 +1,120 @@ +package ch43_topology_sort + +import org.scalatest.{FlatSpec, Matchers} + +class GraphTopologyTest extends FlatSpec with Matchers { + + behavior of "GraphTopologyTest" + + /* + a -> b + | | + \|/ \|/ + c -> d -> e + */ + it should "topologySortByKahn - 1" in { + val nodes = Array("a", "b", "c", "d", "e") + val graphTopology = new GraphTopology(nodes.length) + graphTopology.addEdge(0, 1) + graphTopology.addEdge(0, 2) + graphTopology.addEdge(1, 3) + graphTopology.addEdge(2, 3) + graphTopology.addEdge(3, 4) + + val seq = graphTopology.topologySortByKahn() + seq.map(nodes(_)).mkString(",") should equal("a,b,c,d,e") + } + + /* + a -> d --- + | | + \|/ \|/ + e -> c + */ + it should "topologySortByKahn - 2" in { + val nodes = Array("a", "b", "c", "d", "e") + val graphTopology = new GraphTopology(nodes.length) + graphTopology.addEdge(0, 3) + graphTopology.addEdge(3, 4) + graphTopology.addEdge(3, 2) + graphTopology.addEdge(4, 2) + + val seq = graphTopology.topologySortByKahn() + seq.map(nodes(_)).mkString(",") should equal("a,b,d,e,c") + } + + /* + a -> d <- b + | /|\ + \|/ | + e -> c + */ + it should "topologySortByKahn - 3" in { + val nodes = Array("a", "b", "c", "d", "e") + val graphTopology = new GraphTopology(nodes.length) + graphTopology.addEdge(0, 3) + graphTopology.addEdge(3, 4) + graphTopology.addEdge(4, 2) + graphTopology.addEdge(2, 1) + graphTopology.addEdge(1, 3) + + val seq = graphTopology.topologySortByKahn() + seq.map(nodes(_)).mkString(",") should equal("a") + } + + /* + a -> b + | | + \|/ \|/ + c -> d -> e + */ + it should "topologySortByDFS - 1" in { + val nodes = Array("a", "b", "c", "d", "e") + val graphTopology = new GraphTopology(nodes.length) + graphTopology.addEdge(0, 1) + graphTopology.addEdge(0, 2) + graphTopology.addEdge(1, 3) + graphTopology.addEdge(2, 3) + graphTopology.addEdge(3, 4) + + val seq = graphTopology.topologySortByDFS() + seq.map(nodes(_)).mkString(",") should equal("a,b,c,d,e") + } + + /* + a -> d --- + | | + \|/ \|/ + e -> c + */ + it should "topologySortByDFS - 2" in { + val nodes = Array("a", "b", "c", "d", "e") + val graphTopology = new GraphTopology(nodes.length) + graphTopology.addEdge(0, 3) + graphTopology.addEdge(3, 4) + graphTopology.addEdge(3, 2) + graphTopology.addEdge(4, 2) + + val seq = graphTopology.topologySortByDFS() + seq.map(nodes(_)).mkString(",") should equal("a,b,d,e,c") + } + + /* + a -> d <- b + | /|\ + \|/ | + e -> c + */ + it should "topologySortByDFS - 3" in { + val nodes = Array("a", "b", "c", "d", "e") + val graphTopology = new GraphTopology(nodes.length) + graphTopology.addEdge(0, 3) + graphTopology.addEdge(3, 4) + graphTopology.addEdge(4, 2) + graphTopology.addEdge(2, 1) + graphTopology.addEdge(1, 3) + + val seq = graphTopology.topologySortByKahn() + seq.map(nodes(_)).mkString(",") should equal("a") + } +} diff --git a/swift/05_array/MyArray.swift b/swift/05_array/MyArray.swift new file mode 100644 index 00000000..42a135b6 --- /dev/null +++ b/swift/05_array/MyArray.swift @@ -0,0 +1,77 @@ +// +// Created by Jiandan on 2018/10/10. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +// Swift 泛型,此数组支持不同数据类型 +public struct MyArray { + private var data: [Element] + private var capacity = 0 // 数组长度 + private var count = 0 // 已保存的数据个数 + + /// 构造方法 + /// - parameter defaultElement: 默认元素,用来占位 + /// - parameter capacity: 数组长度 + init(defaultElement: Element, capacity: Int) { + data = [Element](repeating: defaultElement, count: capacity) + self.capacity = capacity + } + + // 根据 index,查找元素 + func find(at index: Int) -> Element? { + // index 必须在 [0, count) + guard index >= 0, index < count else { + return nil + } + + return data[index] + } + + // 根据 index,删除元素 + mutating func delete(at index: Int) -> Bool { + // index 必须在 [0, count) + guard index >= 0, index < count else { + return false + } + + // [index, count - 1) 从 index 开始,元素分别向前移动一位 + for i in index ..< count - 1 { + data[i] = data[i+1] + } + count -= 1 + return true + } + + // 根据 index 插入元素 + mutating func insert(value: Element, at index: Int) -> Bool { + // index 必须在 [0, count) + guard index >= 0, index < count, count < capacity else { + return false + } + + // count - 1 ~ index + for i in (index ... count - 1).reversed() { + data[i + 1] = data[i] + } + + data[index] = value + count += 1 + return true + } + + // 添加元素 + mutating func add(value: Element) -> Bool { + guard count < capacity else { + return false + } + data[count] = value + count += 1 + return true + } + + func printAll() { + print("\(data)") + } +} diff --git a/swift/06_linkedlist/SinglyLinkedList.swift b/swift/06_linkedlist/SinglyLinkedList.swift new file mode 100644 index 00000000..a9af410e --- /dev/null +++ b/swift/06_linkedlist/SinglyLinkedList.swift @@ -0,0 +1,128 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright © 2018 Jiandan. All rights reserved. +// + +import Foundation + +class Node { + var value: T? + var next: Node? + + init(){} + + init(value: T) { + self.value = value + } +} + +/// 单链表 +/// 实现插入、删除、查找操作 +class List { + private var dummy = Node() // 哨兵结点,不存储数据 + var size: Int { + var num = 0 + var tmpNode = dummy.next + while tmpNode != nil { + num += 1 + tmpNode = tmpNode!.next + } + return num + } + var isEmpty: Bool { return size > 0 } + + /// find node with value + func node(with value: Element) -> Node? { + var node = dummy.next + while node != nil { + if node!.value == value { + return node + } + node = node!.next + } + + return nil + } + // 约定:链表的 index 从 1 开始 + func node(at index: Int) -> Node? { + var num = 1 + var node = dummy.next + while node != nil { + if num == index { + return node + } + node = node!.next + num += 1 + } + return nil + } + + func insertToHead(value: Element) { + let newNode = Node(value: value) + return insertToHead(node: newNode) + } + + func insertToHead(node: Node) { + node.next = dummy.next + dummy.next = node + } + + func insert(after node: Node, newValue: Element) { + let newNode = Node(value: newValue) + return insert(after: node, newNode: newNode) + } + + func insert(after node: Node, newNode: Node) { + newNode.next = node.next + node.next = newNode + } + + func insert(before node: Node, newValue: Element) { + let newNode = Node(value: newValue) + return insert(before: node, newNode: newNode) + } + + func insert(before node: Node, newNode: Node) { + var lastNode = dummy + var tmpNode = dummy.next + + while tmpNode != nil { + if tmpNode === node { + newNode.next = tmpNode + lastNode.next = newNode + break + } + lastNode = tmpNode! + tmpNode = tmpNode!.next + } + } + + func delete(node: Node) { + var lastNode = dummy + var tmpNode = dummy.next + + while tmpNode != nil { + if tmpNode === node { + lastNode.next = tmpNode!.next + break + } + + lastNode = tmpNode! + tmpNode = tmpNode!.next + } + } + /// 删除首个 value 符合要求的结点 + func delete(value: Element) { + var lastNode = dummy + var tmpNode = dummy.next + while tmpNode != nil { + if tmpNode!.value == value { + lastNode.next = tmpNode!.next + break + } + + lastNode = tmpNode! + tmpNode = tmpNode!.next + } + } +} diff --git a/swift/07_linkedlist/LinkedListAlgo.swift b/swift/07_linkedlist/LinkedListAlgo.swift new file mode 100644 index 00000000..979b8f6e --- /dev/null +++ b/swift/07_linkedlist/LinkedListAlgo.swift @@ -0,0 +1,111 @@ +// +// Created by Jiandan on 2018/10/13. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +/** + * 1) 单链表反转 + * 2) 链表中环的检测 + * 3) 两个有序的链表合并 + * 4) 删除链表倒数第n个结点 + * 5) 求链表的中间结点 + */ +import Foundation +/// 单链表反转 +func reverseSinglyLinkedList(head: Node) -> Node? { + var reverseHead: Node?, currentNode: Node?, prevNode: Node? + currentNode = head + while currentNode != nil { + let nextNode = currentNode!.next + if nextNode == nil { + reverseHead = currentNode + } + currentNode!.next = prevNode + prevNode = currentNode + currentNode = nextNode + } + return reverseHead +} +/// 检测环 +func hasCircle(head: Node) -> Bool { + var fast = head.next + var slow: Node? = head + while fast != nil { + if fast === slow { + return true + } + fast = fast!.next?.next + slow = slow!.next + } + return false +} +/// 两个有序的链表合并 +func mergeSortedLists(headA: Node?, headB: Node?) -> Node? { + guard let headA = headA else { + return headB + } + guard let headB = headB else { + return headA + } + + var head: Node?, tail: Node? + var nodeA: Node? = headA, nodeB: Node? = headB + if nodeA!.value! < nodeB!.value! { + head = nodeA + nodeA = nodeA!.next + } else { + head = nodeB + nodeB = nodeB!.next + } + tail = head + + while nodeA != nil, nodeB != nil { + if nodeA!.value! < nodeB!.value! { + tail!.next = nodeA + nodeA = nodeA!.next + } else { + tail!.next = nodeB + nodeB = nodeB!.next + } + tail = tail!.next + } + + if nodeA != nil { + tail?.next = nodeA + } else { + tail?.next = nodeB + } + + return head +} + +/// 删除倒数第n个结点 +func deleteNode(at lastNum: Int, in head: Node) { + var slow: Node? = head + var fast: Node? = head + var num = 1 + while fast != nil, num < lastNum { + fast = fast!.next + num += 1 + } + + var prevNode: Node? + while fast != nil { + prevNode = slow + fast = fast!.next + slow = slow!.next + } + prevNode?.next = slow?.next +} + +/// 求链表的中间结点 +func halfNode(in head: Node) -> Node? { + var slow: Node? = head + var fast: Node? = head + + while fast?.next != nil, fast?.next?.next != nil { + fast = fast!.next?.next + slow = slow!.next + } + return slow +} diff --git a/swift/08_stack/Browser.swift b/swift/08_stack/Browser.swift new file mode 100644 index 00000000..2b740d71 --- /dev/null +++ b/swift/08_stack/Browser.swift @@ -0,0 +1,27 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct Page { + /// 存储前进 url + private var forwardArray = [String]() + /// 存储后退 url + private var backArray = [String]() + + var currentURL: String? { return forwardArray.last } + + init(url: String) { + forwardArray.append(url) + } + /// 前进 + mutating func goForward(url: String) { + forwardArray.append(url) + } + /// 后退 + mutating func goBack() { + backArray.append(forwardArray.popLast()!) + } +} diff --git a/swift/08_stack/BrowserDemo.swift b/swift/08_stack/BrowserDemo.swift new file mode 100644 index 00000000..31303056 --- /dev/null +++ b/swift/08_stack/BrowserDemo.swift @@ -0,0 +1,27 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct Page { + /// 存储前进 url + private var forwardArray = [String]() + /// 存储后退 url + private var backArray = [String]() + + var currentURL: String { return forwardArray.last! } + + init(url: String) { + forwardArray.append(url) + } + /// 前进 + mutating func goForward(url: String) { + forwardArray.append(url) + } + /// 后退 + mutating func goBack() { + backArray.append(forwardArray.popLast()!) + } +} diff --git a/swift/08_stack/Stack.swift b/swift/08_stack/Stack.swift new file mode 100644 index 00000000..65ddcc90 --- /dev/null +++ b/swift/08_stack/Stack.swift @@ -0,0 +1,21 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +protocol Stack { + /// 持有的数据类型 + associatedtype Element + /// 是否为空 + var isEmpty: Bool { get } + /// 队列大小 + var size: Int { get } + /// 返回队列头部元素 + var peek: Element? { get } + /// 入栈 + mutating func push(newElement: Element) -> Bool + /// 出栈 + mutating func pop() -> Element? +} diff --git a/swift/08_stack/StackBasedOnLinkedList.swift b/swift/08_stack/StackBasedOnLinkedList.swift new file mode 100644 index 00000000..d92df90e --- /dev/null +++ b/swift/08_stack/StackBasedOnLinkedList.swift @@ -0,0 +1,39 @@ +// +// Created by Jiandan on 2018/10/12. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct StackBasedOnLinkedList: Stack { + private var head = Node() // 哨兵结点,不存储内容 + + // MARK: Protocol: Stack + + var isEmpty: Bool { return head.next == nil } + + var size: Int { + var count = 0 + var cur = head.next + while cur != nil { + count += 1 + cur = cur?.next + } + return count + } + + var peek: Element? { return head.next?.value } + + func push(newElement: Element) -> Bool { + let node = Node(value: newElement) + node.next = head.next + head.next = node + return true + } + + func pop() -> Element? { + let node = head.next + head.next = node?.next + return node?.value + } +} diff --git a/swift/09_queue/ArrayQueue.swift b/swift/09_queue/ArrayQueue.swift new file mode 100644 index 00000000..d5c5bca7 --- /dev/null +++ b/swift/09_queue/ArrayQueue.swift @@ -0,0 +1,108 @@ +// +// Created by Jiandan on 2018/10/11. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +/// 用数组实现的队列 +struct ArrayQueue: Queue { + /// 数组 + private var items: [Element] + /// 数组最大长度 + private var capacity = 0 + /// 队头下标 + private var head = 0 + /// 队尾下标 + private var tail = 0 + + /// 构造方法 + /// - parameter defaultElement: 默认元素 + /// - parameter capacity: 数组长度 + init(defaultElement: Element, capacity: Int) { + self.capacity = capacity + items = [Element](repeating: defaultElement, count: capacity) + } + + // MARK: Protocol: Queue + + var isEmpty: Bool { return head == tail } + + var size: Int { return tail - head } + + var peek: Element? { return isEmpty ? nil : items[head] } + + // 没有数据搬移的实现,即实现了一个有界序列 +// mutating func enqueue(newElement: Element) -> Bool { +// // 整个队列都占满了 +// if tail == capacity { +// return false +// } +// +// items[tail] = newElement +// tail += 1 +// return true +// } + // 有数据搬移的实现,即实现了一个无界序列 + mutating func enqueue(newElement: Element) -> Bool { + // 如果 tail == capacity 表示队列末尾没有空间了 + if tail == capacity { + // 整个队列都占满了 + if head == 0 { return false } + // 数据搬移 + for i in head ..< tail { + items[i - head] = items[i] + } + // 搬移完之后重新更新 head 和 tail + tail -= head + head = 0 + } + + items[tail] = newElement + tail += 1 + return true + } + + mutating func dequeue() -> Element? { + if isEmpty { + return nil + } + + let item = items[head] + head += 1 + return item + } +} + +/// 使用2个数组实现无界队列,用到 Swift 中 Array 较多的方法 +/// 来源:《iOS 面试之道》(故胤道长,唐巧) +struct ArrayQueue2: Queue { + /// 输入数组,主要负责入队 + var inArray = [Element]() + /// 输出数组,主要负责出队 + var outArray = [Element]() + + var isEmpty: Bool { return inArray.isEmpty && outArray.isEmpty } + + var size: Int { return inArray.count + outArray.count } + + // 当 outArray 为空时,返回 inArray 首个元素,否则返回 outArray 末尾元素 + var peek: Element? { return outArray.isEmpty ? inArray.first : outArray.last } + + mutating func enqueue(newElement: Element) -> Bool { + // inArray 添加元素 + inArray.append(newElement) + return true + } + + mutating func dequeue() -> Element? { + if outArray.isEmpty { + // 将 inArray 倒序存入 outArray 中 + outArray = inArray.reversed() + // 清空 inArray + inArray.removeAll() + } + // 弹出 outArray 最后一个元素 + return outArray.popLast() + } +} diff --git a/swift/09_queue/CircularQueue.swift b/swift/09_queue/CircularQueue.swift new file mode 100644 index 00000000..c0c1e44e --- /dev/null +++ b/swift/09_queue/CircularQueue.swift @@ -0,0 +1,61 @@ +// +// Created by Jiandan on 2018/10/11. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +/// 循环队列 +struct CircularQueue: Queue { + /// 数组 + private var items: [Element] + /// 数组最大长度 + private var capacity = 0 + /// 队头下标 + private var head = 0 + /// 队尾下标 + private var tail = 0 + + /// 构造方法 + /// - parameter defaultElement: 默认元素 + /// - parameter capacity: 数组长度 + init(defaultElement: Element, capacity: Int) { + self.capacity = capacity + items = [Element](repeating: defaultElement, count: capacity) + } + + // MARK: Protocol: Queue + + var isEmpty: Bool { return head == tail } + + var size: Int { + if tail >= head { + return tail - head + } else { + return (tail + 1) + (capacity - head) + } + } + + var peek: Element? { return isEmpty ? nil : items[head] } + + mutating func enqueue(newElement: Element) -> Bool { + // 整个队列都占满了 + if (tail + 1) % capacity == head { + return false + } + + items[tail] = newElement + tail = (tail + 1) % capacity + return true + } + + mutating func dequeue() -> Element? { + if isEmpty { + return nil + } + + let item = items[head] + head = (head + 1) % capacity + return item + } +} diff --git a/swift/09_queue/Queue.swift b/swift/09_queue/Queue.swift new file mode 100644 index 00000000..ab0bdd94 --- /dev/null +++ b/swift/09_queue/Queue.swift @@ -0,0 +1,21 @@ +// +// Created by Jiandan on 2018/10/11. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +protocol Queue { + /// 持有的数据类型 + associatedtype Element + /// 是否为空 + var isEmpty: Bool { get } + /// 队列大小 + var size: Int { get } + /// 返回队列头部元素 + var peek: Element? { get } + /// 入队 + mutating func enqueue(newElement: Element) -> Bool + /// 出队 + mutating func dequeue() -> Element? +} diff --git a/swift/09_queue/QueueBasedOnLinkedList.swift b/swift/09_queue/QueueBasedOnLinkedList.swift new file mode 100644 index 00000000..be8b66c9 --- /dev/null +++ b/swift/09_queue/QueueBasedOnLinkedList.swift @@ -0,0 +1,55 @@ +// +// Created by Jiandan on 2018/10/11. +// Copyright (c) 2018 Jiandan. All rights reserved. +// + +import Foundation + +struct QueueBasedOnLinkedList: Queue { + /// 队首 + var head: Node? + /// 队尾 + var tail: Node? + + // MARK: Protocol: Queue + + var isEmpty: Bool { return head == nil } + + var size: Int { + if isEmpty { + return 0 + } + + var count = 1 // head 本身算一个 + while head?.next != nil { + count += 1 + } + + return count + } + + var peek: Element? { return head?.value } + + mutating func enqueue(newElement: Element) -> Bool { + if isEmpty { + // 空队列 + let node = Node(value: newElement) + head = node + tail = node + } else { + tail!.next = Node(value: newElement) + tail = tail!.next + } + return true + } + + mutating func dequeue() -> Element? { + if isEmpty { + return nil + } + + let node = head + head = head!.next + return node?.value + } +} diff --git a/swift/11_sorts/Sorts.swift b/swift/11_sorts/Sorts.swift new file mode 100644 index 00000000..ac1b68d8 --- /dev/null +++ b/swift/11_sorts/Sorts.swift @@ -0,0 +1,81 @@ +import Foundation + +/// 冒泡排序 +/// +/// - Parameter elements: 数组 +/// - Returns: 返回值 +public func bubbleSort(_ elements: [T]) ->[T] where T: Comparable { + var array = elements + guard array.count > 1 else { + return array + } + for i in 0.. array[j+1] { + array.swapAt(j+1, j) + // 此次冒泡有数据交换 + flag = true + } + } + if (!flag) { + break + } + } + return array +} + + +/// 插入排序 +/// +/// - Parameter elements: 数组 +/// - Returns: 返回值 +public func insertionSort(_ elements: [T]) -> [T] where T: Comparable { + var array = elements + guard array.count > 1 else { + return array + } + for i in 1.. value { + array[p+1] = array[p] + } else { + break + } + } + array[j+1] = value + } + return array +} + + +/// 选择排序 +/// +/// - Parameter elements: 数组 +/// - Returns: 返回值 +public func selectionSort(_ elements: [T]) -> [T] where T: Comparable { + var array = elements + guard array.count > 1 else { + return array + } + for i in 0..(_ a: inout T) where T.Element: Comparable { + quickSort(&a, from: a.startIndex, to: a.index(before: a.endIndex)) +} + +fileprivate func quickSort(_ a: inout T, from low: T.Index, to high: T.Index) where T.Element: Comparable { + if low >= high { return } + + let m = partition(&a, from: low, to: high) + quickSort(&a, from: low, to: a.index(before: m)) + quickSort(&a, from: a.index(after: m), to: high) +} + +fileprivate func partition(_ a: inout T, from low: T.Index, to high: T.Index) -> T.Index where T.Element: Comparable { + let pivot = a[low] + var j = low + var i = a.index(after: low) + while i <= high { + if a[i] < pivot { + a.formIndex(after: &j) + a.swapAt(i, j) + } + a.formIndex(after: &i) + } + a.swapAt(low, j) + return j +} diff --git a/swift/12_sorts/SortsTests.swift b/swift/12_sorts/SortsTests.swift new file mode 100644 index 00000000..7100aedf --- /dev/null +++ b/swift/12_sorts/SortsTests.swift @@ -0,0 +1,58 @@ +// +// SortsTests.swift +// SortsTests +// +// Created by Wenru Dong on 2018/10/14. +// Copyright © 2018年 Wenru Dong. All rights reserved. +// + +import XCTest + +class SortsTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testMergeSort() { + var a1 = [1, 1, 1, 1] + mergeSort(&a1) + XCTAssertEqual(a1, [1, 1, 1, 1]) + + var a2 = [4, 3, 2, 1] + mergeSort(&a2) + XCTAssertEqual(a2, [1, 2, 3, 4]) + + var a3 = [3, 6, 9, 7, 8, -1, 9, 3, -2, 0] + mergeSort(&a3) + XCTAssertEqual(a3, [-2, -1, 0, 3, 3, 6, 7, 8, 9, 9]) + } + + func testQuickSort() { + var a1 = [1, 1, 1, 1] + quickSort(&a1) + XCTAssertEqual(a1, [1, 1, 1, 1]) + + var a2 = [4, 3, 2, 1] + quickSort(&a2) + XCTAssertEqual(a2, [1, 2, 3, 4]) + + var a3 = [3, 6, 9, 7, 8, -1, 9, 3, -2, 0] + quickSort(&a3) + XCTAssertEqual(a3, [-2, -1, 0, 3, 3, 6, 7, 8, 9, 9]) + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/swift/12_sorts/mergeSort.swift b/swift/12_sorts/mergeSort.swift new file mode 100644 index 00000000..7050164f --- /dev/null +++ b/swift/12_sorts/mergeSort.swift @@ -0,0 +1,52 @@ +// +// sorts.swift +// algo +// +// Created by Wenru Dong on 2018/10/14. +// Copyright © 2018年 Wenru Dong. All rights reserved. +// + +import Foundation + +public func mergeSort(_ a: inout T) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable { + mergeSort(&a, from: a.startIndex, to: a.index(before: a.endIndex)) +} + +fileprivate func mergeSort(_ a: inout T, from low: T.Index, to high: T.Index) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable { + if low >= high { return } + let dist = a.distance(from: low, to: high) + let mid = a.index(low, offsetBy: dist/2) + mergeSort(&a, from: low, to: mid) + mergeSort(&a, from: a.index(mid, offsetBy: 1), to: high) + merge(&a, from: low, through: mid, to: high) +} + +fileprivate func merge(_ a: inout T, from low: T.Index, through mid: T.Index, to high: T.Index) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable { + var i = low + var j = a.index(mid, offsetBy: 1) + var tmp = Array() + tmp.reserveCapacity(a.distance(from: low, to: high) + 1) + while i <= mid && j <= high { + if a[i] <= a[j] { + tmp.append(a[i]) + a.formIndex(after: &i) + } else { + tmp.append(a[j]) + a.formIndex(after: &j) + } + } + + var start = i + var end = mid + if j <= high { + start = j + end = high + } + tmp.append(contentsOf: a[start...end]) + + var current = low + for element in tmp { + a[current] = element + a.formIndex(after: ¤t) + } +} diff --git a/swift/14_sorts/CountingSort.swift b/swift/14_sorts/CountingSort.swift new file mode 100644 index 00000000..fc0dc936 --- /dev/null +++ b/swift/14_sorts/CountingSort.swift @@ -0,0 +1,33 @@ +// +// CountingSort.swift +// algo +// +// Created by Wenru Dong on 2018/10/18. +// Copyright © 2018年 Wenru Dong. All rights reserved. +// + +import Foundation + +// 假设数组中储存的都是非负整数 +public func countingSort(_ a: inout [Int]) { + if a.count <= 1 { return } + + var counts = Array(repeating: 0, count: a.max()! + 1) + for num in a { + counts[num] += 1 + } + for i in 1.. { + private cacheMap: Map> + private readonly limit: number + private head: LinkedListNode | null = null + private end: LinkedListNode | null = null + + constructor(limit: number) { + if (limit <= 0) throw new Error('limit of cache must > 0') + this.cacheMap = new Map() + this.limit = limit + } + + public get(key: K): V | null { + const node = this.cacheMap.get(key) + if (!node) return null + this.refreshNode(node) + return node.value + } + + public put(key: K, value: V) { + const node = this.cacheMap.get(key) + // 原缓存不存在则加入到队尾 + if (!node) { + // 大于规定的size则删除最不常用的 + if (this.cacheMap.size >= this.limit) { + const oldKey = this.removeNode(this.head!) + this.cacheMap.delete(oldKey) + } + // 在队尾添加 + const newNode = new LinkedListNode(key, value) + this.addNode(newNode) + this.cacheMap.set(key, newNode) + } else { + node.value = value + this.refreshNode(node) + } + } + + private refreshNode(node: LinkedListNode) { + if (node === this.end) return + this.removeNode(node) + this.addNode(node) + } + + private removeNode(node: LinkedListNode): K { + if (node === this.end) { + this.end = this.end.prev + } else if (node === this.head) { + this.head = this.head.next + } else { + // 这个由于排除了首尾节点 + node.prev!.next = node.next + node.next!.prev = node.prev + } + return node.key + } + + /** + * 这里向尾部追加节点 + * @param node + */ + private addNode(node: LinkedListNode) { + if (this.end) { + this.end.next = node + node.prev = this.end + } + this.end = node + if (this.head === null) { + this.head = node + } + // 消除之前的节点的下一个引用对象,防止无限循环 + node.next = null + } +} + +class LinkedListNode { + key: K + value: V + next: LinkedListNode | null + prev: LinkedListNode | null + + constructor( + key: K, + value: V, + next: LinkedListNode | null = null, + prev: LinkedListNode | null = null + ) { + this.key = key + this.value = value + this.next = next + this.prev = prev + } +} + +const cache = new LRUCache(3) +cache.put('lv','xzw') +cache.put('lv2','xzw2') +cache.put('lv3','xzw3') +cache.put('lv4','xzw4') +cache.put('lv5','xzw5') + +console.log(cache) diff --git a/typescript/06_linkedlist/LinkedList.ts b/typescript/06_linkedlist/LinkedList.ts new file mode 100644 index 00000000..ed2031c3 --- /dev/null +++ b/typescript/06_linkedlist/LinkedList.ts @@ -0,0 +1,132 @@ +/** + * 双向链表,更加常用设计也更加复杂一些 + * 需要更多的存储空间和操作复杂度 + */ +import List from './List' + +class LinkedList implements List { + size: number = 0 + private head: LinkedListNode | null = null + private last: LinkedListNode | null = null + + findByIndex(index: number): LinkedListNode | null { + let p = this.head + let pos = 0 + while (p && pos !== index) { + p = p.next + pos++ + } + return p + } + + findByValue(value: T): LinkedListNode | null { + let p = this.head + while (p && p.item !== value) { + p = p.next + } + return p + } + + insertToHead(value: T): void { + let p = this.head + const newNode = new LinkedListNode(value) + // 没有元素的时候需要初始化头节点和尾节点 + if (!p) { + this.last = this.head = newNode + } else { + p.prev = newNode + newNode.next = p + this.head = newNode + } + this.size++ + } + + /** + * 在指定的index后面插入节点 + * @param value 节点的值 + * @param index 指定的位置 + */ + insertToIndex(value: T, index: number): void { + let p = this.head + let pos = 0 + const newNode = new LinkedListNode(value) + while (p !== null && pos !== index) { + p = p.next + pos++ + } + if (p === null) return + newNode.next = p.next + p.next = newNode + newNode.prev = p + this.size++ + } + + insertToTail(value: T): void { + let p = this.last + const newNode = new LinkedListNode(value) + if (p === null) { + this.head = this.last = newNode + } else { + p.next = newNode + newNode.prev = p + this.last = newNode + } + + this.size++ + } + + remove(value: T): boolean { + let p = this.head + while (p && p.item !== value) { + p = p.next + } + if (!p) return false + if (p.prev) { + p.prev.next = p.next + } else { + this.head = p.next + } + if (p.next) { + p.next.prev = p.prev + } else { + this.last = p.prev + } + this.size-- + return true + } + + toString(): string { + let ret: string = '' + let p = this.head + while (p) { + ret = `${ret} ${p.item} ` + p = p.next + } + return ret + } +} + +class LinkedListNode { + item: T + next: LinkedListNode | null + prev: LinkedListNode | null + + constructor( + item: T, + next: LinkedListNode | null = null, + prev: LinkedListNode | null = null + ) { + this.item = item + this.next = next + this.prev = prev + } +} + +const linkedList = new LinkedList() +linkedList.insertToHead('12') +linkedList.insertToHead('haha') +linkedList.insertToHead('www') +linkedList.insertToTail('zxc') +linkedList.insertToIndex('12ooo', 0) +linkedList.remove('12oooo') +console.log(linkedList.toString()) diff --git a/typescript/06_linkedlist/List.ts b/typescript/06_linkedlist/List.ts new file mode 100644 index 00000000..c6b7271c --- /dev/null +++ b/typescript/06_linkedlist/List.ts @@ -0,0 +1,19 @@ +interface List { + insertToHead(value: T): void + + findByValue(value: T): any + + findByIndex(index: number): any + + insertToIndex(value: T, index: number): void + + remove(value: T): boolean + + insertToHead(value: T): void + + insertToTail(value: T): void + + toString(): string +} + +export default List diff --git a/typescript/06_linkedlist/SingleLinkedList.ts b/typescript/06_linkedlist/SingleLinkedList.ts new file mode 100644 index 00000000..5be3c921 --- /dev/null +++ b/typescript/06_linkedlist/SingleLinkedList.ts @@ -0,0 +1,121 @@ +/** + * 1)单链表的插入、删除、查找操作; + * 2)链表支持任意类型数据 + */ +import List from './List' + +class SingleLinkedList implements List { + // 哨兵头节点 + private readonly head: SingleNode + + constructor() { + this.head = new SingleNode(null) + } + + public findByValue(value: T): SingleNode | null { + let p = this.head + while (p.next != null) { + if (p.next.value === value) return p.next + p = p.next + } + return p.next + } + + public findByIndex(index: number): SingleNode | null { + let p = this.head + let pos = 0 + while (p.next != null && pos !== index) { + p = p.next + pos++ + } + return p.next + } + + /** + * 向指定的位置插入节点 + * @param value + * @param index + */ + public insertToIndex(value: T, index: number): void { + const newNode = new SingleNode(value) + let p = this.head + let pos = 0 + while (p.next != null && pos !== index) { + p = p.next + pos++ + } + if (p.next == null) return + newNode.next = p.next.next + p.next.next = newNode + } + + /** + * 根据值去删除节点 + * @param value 1 表示删除成功,0 表示删除失败 + */ + public remove(value: T): boolean { + let p = this.head + while (p.next != null) { + if (p.next.value === value) break + p = p.next + } + if (p.next === null) return false + p.next = p.next.next + return true + } + + public insertToHead(value: T): void { + const newNode = new SingleNode(value, null) + this.insertNodeToHead(newNode) + } + + public insertToTail(value: T): void { + const newNode = new SingleNode(value, null) + this.insertNodeToTail(newNode) + } + + private insertNodeToHead(node: SingleNode): void { + node.next = this.head.next + this.head.next = node + } + + public toString(): string { + let ret: string = '' + let p = this.head + while (p.next != null) { + ret = `${ret} ${p.next.value} ` + p = p.next + } + return ret + } + + /** + * 单链表的尾插入比较费时 + * @param newNode 插入的新节点 + */ + private insertNodeToTail(newNode: SingleNode): void { + let p = this.head + while (p.next != null) { + p = p.next + } + p.next = newNode + } +} + +class SingleNode { + public value: T + public next: SingleNode | null + + constructor(value: T, next: SingleNode | null = null) { + this.value = value + this.next = next + } +} + +const singleLinkedList = new SingleLinkedList() +singleLinkedList.insertToTail('god') +singleLinkedList.insertToTail('my') +// console.log(singleLinkedList.printLinkedList()) +singleLinkedList.insertToIndex('haha', 1) +singleLinkedList.remove('ha1') +singleLinkedList.toString() diff --git a/typescript/07_linkedlist/LinkedListAlog.ts b/typescript/07_linkedlist/LinkedListAlog.ts new file mode 100644 index 00000000..0e32248f --- /dev/null +++ b/typescript/07_linkedlist/LinkedListAlog.ts @@ -0,0 +1,133 @@ +/** + * 单链表的常见操作包括 + * 链表反转 + * 链表中环的检测 + * 有序链表的合并 + * 删除链表倒数第n个节点 + * 链表中间节点 + */ +class LinkedListAlog { + /** + * 反转链表,依次将节点插入到头部 + * @param list + */ + public static reverse(list: SingleNode): SingleNode | null { + let currentNode: SingleNode | null = list + let prevNode = null + while (currentNode) { + const nextNode: SingleNode | null = currentNode.next + currentNode.next = prevNode + prevNode = currentNode + currentNode = nextNode + } + return prevNode + } + + /** + * 通过快慢指针来检测是否为一个环 + * @param list + */ + public static checkCircle(list: SingleNode): boolean { + if (!list) return false + let fast: SingleNode | null = list.next + let slow: SingleNode | null = list + while (fast && fast.next) { + fast = fast.next.next + slow = slow!.next + if (fast === slow) return true + } + return false + } + + /** + * 倒序删除节点 + * @param list + * @param index + */ + public static removeFromEnd(list: SingleNode, index: number): SingleNode | null { + // 如果是个环,就没必要找了 + if (this.checkCircle(list)) return list + let newNode = this.reverse(list) + let preNode = null + let pos = 0 + while (newNode && pos !== index) { + newNode = newNode.next + pos++ + preNode = newNode + } + if (!newNode) return null + if (preNode) { + preNode.next = newNode.next + } + return this.reverse(newNode) + } + + public static findMidNode(list: SingleNode): SingleNode | null { + if (!list) return null + let fast = list.next + let slow = list + while (fast && fast.next) { + fast = fast.next.next + slow = slow.next! + } + return slow + } + + /** + * 有序链表的合并,根据不同的值进行插入 + * @param a + * @param b + */ + public static mergeSortedLists(a: SingleNode, b: SingleNode): SingleNode | null { + if (!a || !b) return a ? a : b + let p: SingleNode | null = a + let q: SingleNode | null = b + // 新链表的头部指针 + let newList: SingleNode | null = null + if (p.value < q.value) { + newList = p + p = p.next + } else { + newList = q + q = q.next + } + let currNode = newList + while (p && q) { + if (p.value < q.value) { + currNode.next = p + p = p.next + } else { + currNode.next = q + q = q.next + } + currNode = currNode.next + } + if (p) { + currNode.next = p + } else { + currNode.next = q + } + return newList + } +} + +class SingleNode { + public value: T + public next: SingleNode | null + + constructor(value: T, next: SingleNode | null = null) { + this.value = value + this.next = next + } +} + +const node1 = new SingleNode(1) +node1.next = new SingleNode(3) +node1.next.next = new SingleNode(5) + +const node2 = new SingleNode(2) +node2.next = new SingleNode(7) +node2.next.next = new SingleNode(8) +node2.next.next.next = new SingleNode(10) + +console.log(LinkedListAlog.findMidNode(node1)) diff --git a/typescript/08_stack/StackAndBrowser.ts b/typescript/08_stack/StackAndBrowser.ts new file mode 100644 index 00000000..51778cd4 --- /dev/null +++ b/typescript/08_stack/StackAndBrowser.ts @@ -0,0 +1,112 @@ +/** + * 基于单向链表实现栈结构 + */ +export class Stack { + private node: LinkedNode | null = null + size: number = 0 + + public push(value: T) { + if (!value) return + const newNode = new LinkedNode(value) + if (!this.node) { + this.node = newNode + } else { + newNode.next = this.node + this.node = newNode + } + this.size++ + } + + public pop(): T | null { + if (!this.node) { + return null + } + const value = this.node.value + this.node = this.node.next + this.size-- + return value + } +} + +/** + * 单向链表 + */ +class LinkedNode { + value: T + next: LinkedNode | null + + constructor(value: T, next: LinkedNode | null = null) { + this.value = value + this.next = next + } +} + +/** + * 使用双栈结构实现浏览器的前进后退 + */ +class Browser { + // 存放后退的所有历史url + private backStack: Stack + // 存放前进的所有url + private forwardStack: Stack + private current: T + + constructor(current: T) { + this.backStack = new Stack() + this.forwardStack = new Stack() + this.current = current + } + + public back(): T | null { + if (this.backStack.size > 0) { + this.forwardStack.push(this.current) + this.current = this.backStack.pop()! + return this.getCurrentPage() + } + return null + } + + public forward(): T | null { + if (this.forwardStack.size > 0) { + this.backStack.push(this.current) + this.current = this.forwardStack.pop()! + return this.getCurrentPage() + } + return null + } + + /** + * 在网页上点击一个链接 + * @param value + */ + public linkUrl(value: T) { + this.current && this.backStack.push(this.current) + this.current = value + } + + public getCurrentPage(): T { + return this.current + } +} + +const browser = new Browser('www.baidu.com') +browser.linkUrl('www.yuanzhoucehui.com') +browser.linkUrl('www.github.com/jsrdxzw') +// browser.back() +// www.github.com/jsrdxzw +console.log(browser.getCurrentPage()) +browser.back() +// www.yuanzhucehui.com +console.log(browser.getCurrentPage()) +browser.back() +// www.baidu.com +console.log(browser.getCurrentPage()) +browser.back() +// www.baidu.com +console.log(browser.getCurrentPage()) +browser.forward() +// www.yuanzhucehui.com +console.log(browser.getCurrentPage()) +browser.forward() +// www.github.com/jsrdxzw +console.log(browser.getCurrentPage()) diff --git a/typescript/09_queue/CircularQueue.ts b/typescript/09_queue/CircularQueue.ts new file mode 100644 index 00000000..849f6df0 --- /dev/null +++ b/typescript/09_queue/CircularQueue.ts @@ -0,0 +1,51 @@ +/** + * 基于数组的循环队列 + * 为了方便判断队列为空的情况,这里最好引入元素个数 + */ +class CircularQueue { + private items: T[] = [] + private readonly n: number = 0 + private head: number = 0 + private tail: number = 0 + // 队列的实际元素大小 + size: number = 0 + + + constructor(capacity: number) { + this.n = capacity + } + + public enqueue(item: T): boolean { + // 表示队列已经满了 + if (this.size === this.n) return false + this.items[this.tail] = item + this.tail = (this.tail + 1) % this.n + this.size++ + return true + } + + public dequeue(): T | null { + if (!this.size) return null + const item = this.items[this.head] + this.head = (this.head + 1) % this.n + this.size-- + return item + } +} + +const circularQueue = new CircularQueue(3) +circularQueue.enqueue(1) +circularQueue.enqueue(2) +circularQueue.enqueue(3) +circularQueue.enqueue(4) + +const value = circularQueue.dequeue() +const value1 = circularQueue.dequeue() +const value2 = circularQueue.dequeue() +const value3 = circularQueue.dequeue() + +// null +console.log(value3) +// 0 +console.log(circularQueue.size) + diff --git a/typescript/09_queue/README.md b/typescript/09_queue/README.md new file mode 100644 index 00000000..8791f1e8 --- /dev/null +++ b/typescript/09_queue/README.md @@ -0,0 +1,12 @@ +### 队列 + +由于js语言天生就可以使用array数组来实现栈和队列等结构: +```javascript + // 模拟数组队列 + const queue = [] + queue.push(1) // 向数组尾部添加数据 + queue.shift() //数组头部去除数据,并返回 +``` +这里我们使用链表来实现队列结构 + +队列分为无限队列和循环队列 diff --git a/typescript/09_queue/SimpleQueue.ts b/typescript/09_queue/SimpleQueue.ts new file mode 100644 index 00000000..fb50990d --- /dev/null +++ b/typescript/09_queue/SimpleQueue.ts @@ -0,0 +1,62 @@ +/** + * 使用链表实现简单队列 + */ +class SimpleQueue { + private head: LinkedNode | null = null + private tail: LinkedNode | null = null + + /** + * 入队,插入队尾 + * @param value + */ + public enqueue(value: T) { + if (!this.tail) { + this.head = this.tail = new LinkedNode(value) + } else { + const newNode = new LinkedNode(value) + this.tail.next = newNode + this.tail = newNode + } + } + + /** + * 出队,在队头删除 + */ + public dequeue(): T | null { + if (!this.head) return null + const value = this.head.value + this.head = this.head.next + return value + } + + public printAll(): string { + let p = this.head + let res = '' + while (p) { + res = `${res} ${p.value}` + p = p.next + } + return res + } +} + +/** + * 单向链表 + */ +class LinkedNode { + value: T + next: LinkedNode | null + + constructor(value: T, next: LinkedNode | null = null) { + this.value = value + this.next = next + } +} + +const queue = new SimpleQueue() +queue.enqueue(1) +queue.enqueue(2) +queue.enqueue(3) +queue.dequeue() +queue.dequeue() +console.log(queue.printAll()) diff --git a/typescript/10_recursive/climbStairs.ts b/typescript/10_recursive/climbStairs.ts new file mode 100644 index 00000000..e96da278 --- /dev/null +++ b/typescript/10_recursive/climbStairs.ts @@ -0,0 +1,55 @@ +/** + * 递归求解爬楼梯问题 + */ + +/** + * 最开始版本的递归算法 + * @param n + */ +function fn(n: number): any { + if (n === 1) return 1 + if (n === 2) return 2 + return fn(n - 1) + fn(n - 2) +} + +const res1 = fn(10) +// 89 +console.log(res1) + +/** + * 使用depth结合js的闭包特性实现限制函数调用次数的功能 + * @param depth 递归的深度 + */ +function fnWithDepth(depth: number) { + return function fn(n: number): any { + depth++ + if (depth > 1000) throw new Error('function stack is too deep!') + if (n === 1) return 1 + if (n === 2) return 2 + return fn(n - 1) + fn(n - 2) + } +} + +const res2 = fnWithDepth(3)(10) +// 89 +console.log(res2) + +/** + * 通过map来存储已经计算过的值,避免递归重复计算 + */ +function fnWithMap() { + const map = new Map() + return function fn(n: number): any { + if (n === 1) return 1 + if (n === 2) return 2 + if (map.has(n)) { + return map.get(n) + } + const ret = fn(n - 1) + fn(n - 2) + map.set(n, ret) + return ret + } +} + +const res3 = fnWithMap()(10) +console.log(res3) diff --git a/typescript/11_sorts/simpleSort.ts b/typescript/11_sorts/simpleSort.ts new file mode 100644 index 00000000..a9118456 --- /dev/null +++ b/typescript/11_sorts/simpleSort.ts @@ -0,0 +1,75 @@ +/** + * 简单的排序,分为冒泡和插入排序 + * 注意他们都是稳定的排序,并且是原地排序 + * 一般情况下更推荐使用插入排序,因为它所需要的操作更少 + * 这里使用简单工厂创建我们的排序算法 + */ + +/** + * 排序的枚举类型 + */ +enum SortType { + BubbleSort, + InsertSort +} + +interface SortAlgo { + sort(array: number[]): void +} + +class BubbleSort implements SortAlgo { + sort(array: number[]) { + for (let i = 0; i < array.length; i++) { + let flag = false + for (let j = 0; j < array.length; j++) { + if (array[j] > array[j + 1]) { + const temp = array[j] + array[j] = array[j + 1] + array[j + 1] = temp + flag = true + } + } + if (!flag) { + break + } + } + } +} + +class InsertSort implements SortAlgo { + sort(array: number[]) { + for (let i = 1; i < array.length; i++) { + let j = i - 1 + const temp = array[i] + for (; j >= 0; j--) { + if (array[j] > array[j + 1]) { + array[j + 1] = array[j] + } else { + // 这个说明之前的已经排好了,没必要继续比较 + break + } + } + array[j + 1] = temp + } + } +} + +class SortFactory { + static getSortAlgo(type: SortType): SortAlgo { + switch (type) { + case SortType.BubbleSort: + return new BubbleSort() + case SortType.InsertSort: + return new InsertSort() + default: + throw new Error('unknown sort algorithm type') + } + } +} + +const insertSort = SortFactory.getSortAlgo(SortType.InsertSort) +const test1 = [1, 0, 2, 4, 8, 5, 10] +insertSort.sort(test1) +console.log(test1) + + diff --git a/typescript/12_sorts/KthNum.ts b/typescript/12_sorts/KthNum.ts new file mode 100644 index 00000000..6cf2343a --- /dev/null +++ b/typescript/12_sorts/KthNum.ts @@ -0,0 +1,52 @@ +/** + * O(n)的时间复杂度内求无序数组的第K大元素 + * 如[4,2,5,12,3]的第3大元素就是4 + * 这里也是使用了分治和分区的思想 + */ +class KthNum { + getKthNum(array: number[], k: number): number { + const length = array.length + if (k > length) return -1 + // q+1对应的元素一定比q和之前的元素大,q+1就是第q+1大元素 + // 注意返回的q是数组下标,所以我们要加1才能表示第k个元素 + let q = this.partition(array, 0, array.length - 1) + while (q + 1 !== k) { + if (q + 1 > k) { + q = this.partition(array, 0, q - 1) + } else { + q = this.partition(array, q + 1, length - 1) + } + } + return array[q] + } + + /** + * 这里和快速排序一样 + * @param array 数组的一部分 + * @param p 开始坐标 + * @param r 结束坐标 + */ + private partition(array: number[], p: number, r: number) { + const pivot = array[p] + let index = p + 1 + for (let i = index; i <= r; i++) { + if (array[i] < pivot) { + this.swap(array, index, i) + index++ + } + } + this.swap(array, p, index - 1) + return index - 1 + } + + private swap(array: number[], p: number, q: number) { + const temp = array[p] + array[p] = array[q] + array[q] = temp + } +} + +const testFindSortNum = [4, 2, 5, 12, 3] +const kthNum = new KthNum() +const num = kthNum.getKthNum(testFindSortNum, 3) +console.log(num) diff --git a/typescript/12_sorts/MergeSort.ts b/typescript/12_sorts/MergeSort.ts new file mode 100644 index 00000000..a466e197 --- /dev/null +++ b/typescript/12_sorts/MergeSort.ts @@ -0,0 +1,58 @@ +/** + * 归并排序 + * 稳定排序,稳定的O(nlgn)时间复杂度 + * O(n)的空间复杂度 + * 在小规模数据排序中很常用 + */ +class MergeSort { + public static mergeSort(array: number[]) { + if (!array || !array.length) return + const length = array.length + this.mergeSortInternally(array, 0, length - 1) + } + + static mergeSortInternally(array: number[], p: number, r: number) { + if (p >= r) return + // 严格按照中间值作切分点 + // js中除法需要做取整操作,不然结果有可能是小数 + const q = Math.floor(p + (r - p) / 2) + this.mergeSortInternally(array, p, q) + this.mergeSortInternally(array, q + 1, r) + this.mergeArray(array, p, q, r) + } + + private static mergeArray(a: number[], p: number, q: number, r: number) { + let i = p + let j = q + 1 + let k = 0 + // 定义一个临时数组来存放排序的值 + const tmp: number[] = [] + while (i <= q && j <= r) { + if (a[i] <= a[j]) { + tmp[k++] = a[i++] + } else { + tmp[k++] = a[j++] + } + } + // 判断哪个子数组中有剩余的数据 + let start = i + let end = q + if (j <= r) { + start = j + end = r + } + // 将剩余的数据拷贝到临时数组tmp + while (start <= end) { + tmp[k++] = a[start++] + } + + // 将tmp中的数组拷贝回a[p...r] + for (i = 0; i <= r - p; i++) { + a[p + i] = tmp[i] + } + } +} + +const test4 = [1, 3, 2, 3, 10, 9, 7, 6, 0, 12] +MergeSort.mergeSort(test4) +console.log(test4) diff --git a/typescript/12_sorts/quickSort.ts b/typescript/12_sorts/quickSort.ts new file mode 100644 index 00000000..c1963f8b --- /dev/null +++ b/typescript/12_sorts/quickSort.ts @@ -0,0 +1,46 @@ +/** + * 快速排序是不稳定的排序 + * 原地排序,空间复杂度O(1),比归并排序使用更广泛 + * 平均复杂度基本接近O(nlg(n)) + */ + +export class QuickSort { + static sort(array: number[]): void { + this.sortInternally(array, 0, array.length - 1) + } + private static sortInternally(array: number[], p: number, r: number) { + if (p >= r) return + // 获取分界点 + const q: number = this.partition(array, p, r) + this.sortInternally(array, p, q - 1) + this.sortInternally(array, q + 1, r) + } + private static partition(array: number[], p: number, r: number): number { + /** + * 参考值pivot,小于pivot的放在左边,大于pivot的在右边,最后再把分界点的值和它做交换 + * 这样返回的index一定是值在中间的下标 + */ + const pivot = array[p] + // 分界点 + let index = p + 1 + for (let i = index; i <= r; i++) { + if (array[i] < pivot) { + this.swap(array, index, i) + // 找到了比标记值小的元素就移动分界点 + index++ + } + } + this.swap(array, p, index - 1) + return index - 1 + } + + private static swap(array: number[], p: number, q: number) { + const temp = array[p] + array[p] = array[q] + array[q] = temp + } +} + +const testSort = [1, 3, 2, 3, 10, 9, 7, 6, 0, -12] +QuickSort.sort(testSort) +console.log(testSort) diff --git a/typescript/13_sorts/BucketSort.ts b/typescript/13_sorts/BucketSort.ts new file mode 100644 index 00000000..c7004a2e --- /dev/null +++ b/typescript/13_sorts/BucketSort.ts @@ -0,0 +1,52 @@ +/** + * 桶排序对数据的要求比较高 + * 首先要知道数据的范围 + * 然后根据范围将数据分到小范围的桶中 + * 每个桶采用快速排序 + * 当桶的数量接近数据量大小的时候,时间复杂度为O(n) + */ +import { QuickSort } from '../12_sorts/quickSort' + +class BucketSort { + static sort(array: number[], bucketSize: number = 5) { + const length = array.length + if (length === 0) return array + // 首先要确定数据的范围 + let min = array[0] + let max = array[0] + for (let i = 0; i < length; i++) { + if (array[i] < min) { + min = array[i] + } else if (array[i] > max) { + max = array[i] + } + } + + // 初始化桶,确定桶的数量 + // 因为不能保证正好被整除,需要+1 存放剩余的元素 + const bucketCount = Math.floor((max - min) / bucketSize) + 1 + // 桶是个二维数组 + const buckets = new Array(bucketCount) + for (let i = 0; i < bucketCount; i++) { + buckets[i] = [] + } + + // 利用映射函数将数据分配到各个桶中 + // 这个时间复杂度为O(n) + for (let i = 0; i < length; i++) { + buckets[Math.floor((array[i]-min) / bucketSize)].push(array[i]) + } + array.length = 0 // 返回数组 + for (let i = 0; i < bucketCount; i++) { + // 每个桶里根据具体情况排序,使用插入排序或者快速排序等等 + QuickSort.sort(buckets[i]) + for (let j = 0; j < buckets[i].length; j++) { + array.push(buckets[i][j]); + } + } + } +} + +const bucketTest = [1, 3, 2, 3, 10, 9, 7, 6, 0, -12] +BucketSort.sort(bucketTest) +console.log(bucketTest) diff --git a/typescript/13_sorts/CountingSort.ts b/typescript/13_sorts/CountingSort.ts new file mode 100644 index 00000000..ba8ea3e3 --- /dev/null +++ b/typescript/13_sorts/CountingSort.ts @@ -0,0 +1,51 @@ +/** + * 计数排序 + * 也是线性时间复杂度,和桶排序非常类似 + * 适用于值范围较小的大数据排序 + * 注意值范围需要不小于0,不然需要将数据预处理 + * 并非原地排序 + */ +class CountingSort { + static sort(array: number[]) { + const length = array.length + + // 找到这个数组的最大值 + let max = array[0] + array.forEach((item) => { + if (item > max) { + max = item + } + }) + + // 初始化值范围数组 + const countArray = new Array(max + 1).fill(0, 0, max + 1) + // 先计算每个元素的出现个数 + for (let i = 0; i < length; i++) { + countArray[array[i]] = countArray[array[i]] + 1 + } + // 计算元素的累计出现个数 + for (let i = 1; i <= max; i++) { + countArray[i] = countArray[i - 1] + countArray[i] + } + + // 接下来开始计数排序了 + // 空间还是要申请 + const sortedArray = [] + // 倒序遍历能够达到稳定排序的作用 + for (let i = length - 1; i >= 0; i--) { + // -1是为了填补sortedArray在0的位置,因为countArray在0的位置中一定么有值 + const index = countArray[array[i]] - 1 + sortedArray[index] = array[i] + countArray[array[i]]-- + } + for (let i = 0; i < length; i++) { + array[i] = sortedArray[i] + } + } +} + + +const testSort2 = [1, 3, 2, 3, 10, 9, 7, 6, 0] +CountingSort.sort(testSort2) +console.log(testSort2) + diff --git a/typescript/14_binarysearch/BinarySearch.ts b/typescript/14_binarysearch/BinarySearch.ts new file mode 100644 index 00000000..cda48772 --- /dev/null +++ b/typescript/14_binarysearch/BinarySearch.ts @@ -0,0 +1,28 @@ +/** + * 二分查找适合于连续内存的数组查找 + * 并且是已经排好序的数组 + * 时间复杂度只有log(n) + */ +class BinarySearch { + static bSearch(array: number[], target: number) { + if (!array || array.length === 0) return -1 + const length = array.length + let low = 0 + let high = length - 1 + while (low <= high) { + // 一定是整数,这边的移位运算优先级低于+,-运算符,需要加括号 + const mid = low + ((high - low) >> 1) + if (array[mid] === target) { + return mid + } else if (array[mid] > target) { + high = mid - 1 + } else { + low = mid + 1 + } + } + return -1 + } +} + +const testBinarySearch = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +console.log(BinarySearch.bSearch(testBinarySearch, 10)) diff --git a/typescript/15_binarysearch/BinaryFind.ts b/typescript/15_binarysearch/BinaryFind.ts new file mode 100644 index 00000000..152e6a1f --- /dev/null +++ b/typescript/15_binarysearch/BinaryFind.ts @@ -0,0 +1,115 @@ +/** + * 二分查找法在实际的场合使用不多 + * 我们更多的是看到一些查找法的变种 + */ +class BinaryFind { + /** + * 在数组中查找第一个等于给定值的位置 + * @param array 给定的要查找的范围数组 + * @param target 要查找的目标值 + * @return 目标在数组中的位置 + */ + static findFirstElement(array: number[], target: number): number { + const length = array ? array.length : null + if (!length || length === 0) return -1 + let low = 0 + let high = length - 1 + while (low <= high) { + const mid = low + ((high - low) >> 2) + if (array[mid] > target) { + high = mid - 1 + } else if (array[mid] < target) { + low = mid + 1 + } else { + if (mid === 0 || array[mid - 1] !== target) { + return mid + } else { + high = mid - 1 + } + } + } + return -1 + } + + /** + * 在数组中查找最后一个等于给定值的位置 + * @param array 给定的要查找的范围数组 + * @param target 要查找的目标值 + */ + static findLastElement(array: number[], target: number): number { + const length = array ? array.length : null + if (!length || length === 0) return -1 + let low = 0 + let high = length - 1 + while (low <= high) { + const mid = low + ((high - low) >> 2) + if (array[mid] > target) { + high = mid - 1 + } else if (array[mid] < target) { + low = mid + 1 + // 这里已经是找到相等的元素了 + } else { + if (mid === length - 1 || array[mid + 1] !== target) { + return mid + } else { + low = mid + 1 + } + } + } + return -1 + } + + /** + * 在数组中查找第一个大于等于给定值的位置 + * @param array 给定的要查找的范围数组 + * @param target 要查找的目标值 + */ + static findFirstElementGreaterThanTarget(array: number[], target: number): number { + const length = array ? array.length : null + if (!length || length === 0) return -1 + let low = 0 + let high = length - 1 + while (low <= high) { + const mid = low + ((high - low) >> 2) + if (array[mid] < target) { + low = mid + 1 + } else { + if (mid === 0 || array[mid - 1] < target) { + return mid + } else { + high = mid - 1 + } + } + } + return -1 + } + + /** + * 在数组中查找最后一个小于等于给定值的位置 + * @param array 给定的要查找的范围数组 + * @param target 要查找的目标值 + */ + static findLastElementLessThanTarget(array: number[], target: number): number { + const length = array ? array.length : null + if (!length || length === 0) return -1 + let low = 0 + let high = length - 1 + while (low <= high) { + const mid = low + ((high - low) >> 2) + if (array[mid] > target) { + high = mid - 1 + } else { + if (mid === length - 1 || array[mid + 1] > target) { + return mid + } else { + low = mid + 1 + } + } + } + return -1 + } +} + +const binaryFindTest = [1, 3, 4, 4, 5, 6, 8, 8, 8, 11, 18] +const target = BinaryFind.findLastElementLessThanTarget(binaryFindTest, -1) +console.log(target) diff --git a/typescript/17_skiplist/SkipList.ts b/typescript/17_skiplist/SkipList.ts new file mode 100644 index 00000000..b30a9a15 --- /dev/null +++ b/typescript/17_skiplist/SkipList.ts @@ -0,0 +1,171 @@ +/** + * 跳跃表是Redis使用的底层算法 + * 在增删改查都有近似O(log n)的时间复杂度 + * 哈希表虽然在不产生冲突的情况下是O(1)的时间复杂度 + * 但是随着冲突的增多,所需要的扩容操作还是比较耗时的,综合起来不一定快于跳表 + * 这两种结构可以互相补充 + * 下面摘抄一段来自知乎的话 (https://juejin.im/post/57fa935b0e3dd90057c50fbc) + * 比较跳表和哈希表,平衡树之间的区别 + * skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。 + * 在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。 + * 如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。 + * 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。 + * 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。 + * 如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。 + * 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。 + * 从算法实现难度上来比较,skiplist比平衡树要简单。 + */ +export class SkipList { + // head和tail始终指向最顶层的首位节点,通过链表能访问任何位置 + private head: SkipListNode + private tail: SkipListNode + + // 索引的层数,0表示最底层 + private levelCount = 0 + + // 元素的个数 + private size = 0 + + // private readonly MAX_LEVEL = 16 + + + constructor() { + this.head = new SkipListNode(SkipListNode.negInf, null) + this.tail = new SkipListNode(SkipListNode.posInf, null) + } + + public insert(key: number, value: T): void { + let p: SkipListNode + let q: SkipListNode + + let i: number = 0 + + // 先查找位置 + p = this.findNode(key) + + // 如果跳跃表中的值已经存在了,直接赋值即可 + if (p.key === key) { + p.value = value + return + } + + // 没有该值,则进行插入操作,应该是插在p节点的右边. p -> q -> ? + q = new SkipListNode(key, value) + q.left = p + q.right = p.right + if (p.right) { + p.right.left = q + } + p.right = q + + // 再使用随机数决定是否要向更高层攀升 + while (Math.random() < 0.5) { + // 如果新元素的级别已经达到跳跃表的最大高度,则新建空白层 + if (i >= this.levelCount) { + this.addEmptyLevel() + } + + // 从p向左扫描含有高层节点的节点, 方便节点在每一层插入 + while (!p.up) { + p = p.left! + } + p = p.up + + // 新值对应的索引,这里不需要存value了,因为只需要最底层存value即可 + const z = new SkipListNode(key, null) + + z.left = p + z.right = p.right + if (p.right) { + p.right.left = z + } + p.right = z + + z.down = q + q.up = z + + q = z + i = i + 1 + } + this.size++ + } + + public get(key: number): T | null { + const p = this.findNode(key) + return p.key === key ? p.value : null + } + + public remove(key: number) { + let p: SkipListNode | undefined = this.findNode(key) + if (p.key !== key) return + + while (p != null) { + p.left!.right = p.right + p.right!.left = p.left + p = p.up + } + } + + private addEmptyLevel() { + + const p1: SkipListNode = new SkipListNode(SkipListNode.negInf, null) + const p2: SkipListNode = new SkipListNode(SkipListNode.posInf, null) + + p1.right = p2 + p1.down = this.head + + p2.left = p1 + p2.down = this.tail + + this.head.up = p1 + this.tail.up = p2 + + this.head = p1 + this.tail = p2 + + this.levelCount++ + } + + private findNode(key: number): SkipListNode { + const { head } = this + let p = head + while (true) { + // 从左向右查找,直到右节点的key值大于要查找的key值 + while (p.right && p.right.key !== SkipListNode.posInf && p.right.key <= key) { + p = p.right + } + // 如果有更低层的节点,则向低层移动 + if (p.down) { + p = p.down + } else { + break + } + } + // 这里返回的p的key值,是小于等于要找的key值的 + return p + } +} + +export class SkipListNode { + key: number + value: T | null + up?: SkipListNode + down?: SkipListNode + left?: SkipListNode + right?: SkipListNode + + constructor(key: number, value: T | null) { + this.key = key + this.value = value + } + + // 最小的数,无限接近于0,用于表示左标兵 + static negInf: number = Number.MIN_VALUE + // 最大的数,用于表示右标兵 + static posInf: number = Number.MAX_VALUE +} + +const testSkipList = new SkipList() +testSkipList.insert(12, 'qwe') +testSkipList.insert(3, 'mmm') +console.log(testSkipList.get(3)) diff --git a/typescript/24_treesearch/TreeSearch.ts b/typescript/24_treesearch/TreeSearch.ts new file mode 100644 index 00000000..7a69be6d --- /dev/null +++ b/typescript/24_treesearch/TreeSearch.ts @@ -0,0 +1,129 @@ +/** + * 二叉树的增删查操作 + */ +export class TreeSearch { + private root?: Node + + public find(data: number) { + let p = this.root + while (p != null) { + if (data < p.data) { + p = p.left + } else if (data > p.data) { + p = p.right + } else { + return p + } + } + return p + } + + public insert(data: number) { + if (this.root == null) { + this.root = new Node(data) + return + } + let p = this.root + while (p != null) { + if (data < p.data) { + if (p.left == null) { + p.left = new Node(data) + return + } else { + p = p.left + } + } else { + if (data > p.data) { + if (p.right == null) { + p.right = new Node(data) + return + } else { + p = p.right + } + } + } + } + } + + /** + * 删除的时候分三种情况 + * @param data + */ + public delete(data: number) { + // 先把对应的元素找出来,并且为了删除,也得记录父节点的内存地址 + // 包含了null或者undefined两种情况 + if (this.root == null) return + let p: Node | undefined = this.root + let pp = this.root + while (p != null && p.data !== data) { + pp = p + data > p.data ? p = p.right : p = p.left + } + // 没有找到要删除的元素 + if (p == null) return + // 要删除的元素有两个子节点,需要找到右子树的最小节点,然后赋值 + if (p.left != null && p.right != null) { + // 右子树的最小节点 + let minP = p.right + // 右子树的最小节点的父节点 + let minPP = p + while (minP.left != null) { + minPP = minP + minP = minP.left + } + p.data = minP.data + p = minP + pp = minPP + } + + // 有一个子节点或者没有子节点 + let child + if (p.left != null) { + child = p.left + } else if (p.right != null) { + child = p.right + } else { + child = null + } + if (pp == null) { + this.root = undefined + } else if (pp.left === p) { + pp.left = child! + } else { + pp.right = child! + } + } + + public printAllData(): void { + this.printAll(this.root) + } + + private printAll(node?: Node): void { + if (node == null) return + console.log(node.data) + this.printAll(node.left) + this.printAll(node.right) + } +} + +export class Node { + data: number + left?: Node + right?: Node + + constructor(data: number) { + this.data = data + } +} + +const treeSearch = new TreeSearch() +treeSearch.insert(1) +treeSearch.insert(4) +treeSearch.insert(3) +treeSearch.insert(2) +treeSearch.insert(5) +treeSearch.insert(0) + +treeSearch.delete(4) + +treeSearch.printAllData()