diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 0000000..c397d61
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,45 @@
+# This workflow will build a golang project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
+
+name: Go
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ lint-test-build-linux:
+ name: test-lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Set up Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: '^1.21'
+ check-latest: true
+
+ - name: Cache go module
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.cache/go-build
+ ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-
+
+ - name: Test
+ run: cd go && go test -v ./...
+
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ version: latest
+ args: --timeout 10m0s --verbose
+ working-directory: go
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644
index 0000000..401ab40
--- /dev/null
+++ b/.github/workflows/rust.yml
@@ -0,0 +1,34 @@
+name: Rust
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ test-lint:
+ name: test-lint
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Update local toolchain
+ run: |
+ rustup update
+
+ - name: Toolchain info
+ run: |
+ cargo --version
+ rustc --version
+ cargo clippy --version
+
+ - name: Run tests
+ run: cd rust && cargo test
+
+ - name: Run lint
+ run: cd rust && cargo clippy
diff --git a/.gitignore b/.gitignore
index 3ae055a..6d23611 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
pkg
.idea
-java/.project
+
+/rust/target
+**/*.rs.bk
+Cargo.lock
+/.idea/
+.vscode
diff --git a/README.md b/README.md
index 194f91f..61e4e47 100644
--- a/README.md
+++ b/README.md
@@ -1,76 +1,202 @@
-# DataStructures-And-Algorithms-With-Four-Language
+# The Algorithms in go and rust language
-四种语言(Go,Javascript,Java,C)实现常用数据结构和算法。
+用Go、Rust语言实现常用数据结构和算法。用于学习目的,算法效率不一定最优。
所有代码都经过具体测试运行,具体环境为:
-- 操作系统:ubuntu16.04 64位
-- Go语言版本: Go 1.8.3
-- Javascript执行环境: Nodejs 6.11.3
-- Java版本: Java 1.8.0_25
-- C语言编译器: gcc 4.8.2
+- 操作系统:Ubuntu-latest 64位
+- Go语言版本: Go 1.21
+- Rust语言版本: Rust 1.74
-## 目录
-1. [列表](#user-content-列表) (完成)
-2. [栈](#user-content-栈) (完成)
-3. [队列](#user-content-队列) (完成)
-4. [链表](#user-content-链表) (golang,javascript 完成...)
-5. [字典](#user-content-字典) (golang 完成)
-6. [二叉树和二叉查找树](#user-content-二叉树和二叉查找树) (golang 完成)
-7. [图和图算法](#user-content-图和图算法) (待处理...)
-8. [排序算法](#user-content-排序算法) (待处理...)
-9. [检索算法](#user-content-检索算法) (待处理...)
+## 链表相关
+### 双指针技巧
-### 列表
-列表是非常常见的一种数据结构,比如日常所见的购物清单、待办事项等等。
-它提供了对列表数据的一系列操作,比如:添加、删除、修改、遍历等功能。
-当我们把这样的具体问题抽象成用列表去解决的时候,往往可以简化问题。
+#### 合并两个有序链表
-具体实现: [Go](go/arraylist) 、 [Javascript](javascript/arraylist) 、
-[Java](java/arraylist) 、 [C](c/arraylist)
+Ref:
+[Go实现](/go/linkedlist/merge_two_lists.go)、[Rust实现](/rust/src/linkedlist/merge_two_lists.rs)
-### 栈
-栈也是一种非常常见的数据结构,在计算机的世界里,在计算机的世界里,
-栈是一种很高效的数据结构,因为数据只能在栈顶添加或者删除.
-因此栈也被称为一种后入先出的数据结构.栈的使用遍布程序语言实现的方方面面,
-从表达式求值到函数调用.
+代码中用到了`p1`, `p2`两个指针用于比较两个链表节点的推进,`p`指针用于生成新链表节点的推进。在这个过程中没有数据的复制,只有对指针指向的改变,效率很高。
-具体实现: [Go](go/stack) 、 [Javascript](javascript/stack) 、
-[Java](java/stack) 、 [C](c/stack)
+代码中还用到一个链表的算法题中是很常见的「虚拟头结点」技巧,也就是 `dummy` 节点。如果不使用 dummy
+虚拟节点,代码会复杂一些,需要额外处理指针 `p` 为空的情况。
+>
+什么时候需要用虚拟头结点?当需要创造一条新链表的时候,可以使用虚拟头结点简化边界情况的处理。比如说,把两条有序链表合并成一条新的有序链表,是不是要创造一条新链表?再比如想把一条链表分解成两条链表,是不是也在创造新链表?这些情况都可以使用虚拟头结点简化边界情况的处理。
+>
+> 这个问题还有另外一种解法,就是利用递归,可以去掉两个指针,因为在递归栈中可以自动保存这两个状态。参考上面两个文件中的递归实现。通常来说,递归实现的代码都会更简单,但空间复杂度更高。
+#### 单链表的分解
-### 队列
-队列是一种前进先出的数据结构. 在日常生活中非常常见:比如去银行排队办理业务.
-在计算机中也极其常见, 很多情况下,当有大量任务需要完成时, 就会把任务暂时加入到
-任务队列中, 执行一个删除一个,继续执行下一个任务.
+Ref:
-具体实现: [Go](go/queue) 、[Javascript](javascript/queue) 、
-[Java](java/queue) 、 [C](c/queue)
+[Go实现](/go/linkedlist/partition.go)、[Rust实现](/rust/src/linkedlist/partition.rs)
+我们可以把原链表分成两个小链表,一个链表中的元素大小都小于 x,另一个链表中的元素都大于等于 x,最后再把这两条链表接到一起,就得到了题目想要的结果。
-### 链表
-有时候数组不一定是最佳的组织数据的数据结构,因为数组通常都是固定大小的,当数据填满时,
-再加入新元素就变得很困难。在数组中,添加和删除元素也很麻烦,因为要移动数组中的其他元素。
-因此如果需要频繁的添加或者删除元素,可以考虑使用链表组织数据。
+#### 合并K个有序链表
-具体实现: [Go](go/linkedlist) 、[Javascript](javascript/linkedlist) 、
-[Java](java/linkedlist) 、 [C](c/linkedlist)
+Ref:
+[Go实现](/go/linkedlist/merge_k_lists.go)、[Rust实现](/rust/src/linkedlist/merge_k_lists.rs)
-### 字典
-字典类型(map),是一种KV结构,通过key能够找到对应的value,map类型的特点是查询非常快(O(1)),
-原理是通过一个hash函数能快速的找到对应的key的位置,在实际编程中,map类型几乎无处不在。
+合并 k 个有序链表的逻辑类似合并两个有序链表,难点在于,如何快速得到 k 个节点中的最小节点,接到结果链表上?
-具体实现: [Go](go/hashmap) 、[Javascript](javascript/hashmap) 、
-[Java](java/hashmap) 、 [C](c/hashmap)
+通常的一种想法是可以把链表头放到数组里,可以写一个循环判断,每次找出一个最小值,这样的时间复杂度是`O(N*K)`,N是所有链表的节点数量,K
+是链表个数。
+不过还有一种更高效的方式,可以把K个链表头放入 **优先级队列(二叉堆)**
+中,放入一个最小堆中,每次就能方便的获得K个节点中的最小值,算法复杂度是:`O(N*Log(K))`。
-### 二叉树和二叉查找(搜索)树
-二叉树可以看作是链表的二维扩展,链表的一个节点只有一个next指针,但是二叉树的一个节点可以有两个
-next指针(通常被称作left,right),二维扩展后可以解决一维链表的一些问题,比如查找慢等。
-二叉查找树可以把查找,添加,删除元素的时间复杂度降到O(logN)。
+#### 单链表倒数第K个节点
-具体实现: [Go](go/binarytree) 、[Javascript](javascript/binarytree) 、
-[Java](java/binarytree) 、 [C](c/binarytree)
+Ref:
+
+[Go实现](/go/linkedlist/remove_nth_from_end.go)、[Rust实现](/rust/src/linkedlist/remove_nth_from_end.rs)
+
+要找到链表倒数第k个节点,一般的想法是得知道链表的长度`n`,再用`n-k+1`就得到倒数第k个节点,但是这样需要遍历两次链表,如何在只遍历一次的情况下找到呢?
+
+同样使用双指针技巧,先让一个指针先走`k`
+步,然后第二个指针开始和第一个指针一起走,第一个指针走完这个整个链表,第二个指针的位置刚好就在`n-k+1`的位置上。
+
+> 另一种方法是使用递归,先一次性递归到链表结尾,递归出栈过程中对计数器加一,当计数器达到k时,此时的节点就是要被删除的节点。
+
+#### 单链表中点
+
+Ref:
+
+[Go实现](/go/linkedlist/middle_node.go)、[Rust实现](/rust/src/linkedlist/middle_node.rs)
+
+这个和上面一题类似,只是这次是找中点。同样使用快慢指针技巧,每当慢指针 slow 前进一步,快指针 fast 就前进两步,这样,当 fast
+走到链表末尾时,slow 就指向了链表中点。
+
+#### 判断链表是否包含环,并找出环起点
+
+Ref:
+
+[Go实现](/go/linkedlist/detect_cycle.go)、[Rust实现](/rust/src/linkedlist/detect_cycle.rs)
+
+首先我们得知道链表是否有环,实现方法和上面一题类似,通过快慢指针,慢指针走一步,快指针走两步,如果最终两个指针走到了一起,则证明有环,如果快指针为null,则证明无环。
+
+为什么有环快指针就一定会碰到慢指针?我们随便构造一个有环的链表,就会发现,每次走两步,每经过一次有环处,下一次快指针经过的节点都和上一次不同,所以快慢指针早晚一定会遇到。
+
+如何找到环的起点呢?关键在于当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
+
+#### 两个链表是否相交
+
+Ref:
+
+[Go实现](/go/linkedlist/get_intersection_node.go)、[Rust实现](/rust/src/linkedlist/get_intersection_node.rs)
+
+这题最简单的思路是给每个节点构造HashSet,然后通过HashSet判断是否存在相同节点,但这样空间复杂度较高。
+
+想要通过O(1)的空间复杂度完成就要充分观察相交链表的特点:我们可以通过让两个指针遍历两个链表,想办法让两个指针在相交处相遇,这样就找到了相交节点。
+
+方法一:先分别遍历两个链表,计算出链表长度,将两个长度相减即得到长链表比短链表多出来的长度N,然后让长链表指针先走N步,然后两个指针再一起往前走,两个指针相同了,则找到相交节点。
+
+方法二:分别定义两个指针,用于遍历两个链表,指针1遍历完链表1,则将指针1切换到链表2,对指针2做同样的逻辑,最终当两个指针相同时,则就是相交链表节点。这种方法技巧性更强,不容易想到。
+
+### 递归魔法
+
+#### 反转链表
+
+Ref:
+
+[Go实现](/go/linkedlist/reverse_list.go)、[Rust实现](/rust/src/linkedlist/reverse_list.rs)
+
+这个题目明显可以使用迭代方法,但需要小心处理临时对象的保存和边界条件的处理。
+更简单的方式是使用递归法,通过递归的方式找到最后一个节点,之后从倒数第二个节点开始逐个反转链表。
+
+#### 反转链表前N个节点
+
+[Go实现](/go/linkedlist/reverse_list_n.go)、[Rust实现](/rust/src/linkedlist/reverse_list_n.rs)
+
+这个题目和上一个的区别是:只反转前N个,然后第N个作为新的头节点,原来的头结点的Next节点指向原来的第N+1个节点。
+
+方法是递归到第N个节点开始返回,像之前的逻辑一样反转链表,不同的是需要返回第N个和第N+1个节点,第N个节点作为新的头节点,第N+1个节点用于后续反转的节点指向它。
+
+### K个一组翻转链表
+
+Ref:
+
+[Go实现](/go/linkedlist/reverse_k_group.go)、[Rust实现](/rust/src/linkedlist/reverse_k_group.rs)
+
+链表是一种兼具递归好迭代性质的结构,分析这个问题可以发现此问题具有递归性质。
+
+比如对这个链表调用 `reverseKGroup(head, 2)`,即以 2
+个节点为一组反转链表,如果我们可以把前两个节点翻转,那后面的节点该怎么处理?后面的节点还是相似的一条链表,而且规模(长度)比原来的这条链表更小,这就是
+**子问题**。
+
+我们只需要2个一组翻转后的结果再指向下一个`reverseKGroup(head, 2)`的结果,这样就递归实现了翻转,递归退出条件是:最后的元素不足
+k 个,就保持不变。
+
+### 判断回文链表
+
+Ref:
+
+[Go实现](/go/linkedlist/is_palindrome.go)、[Rust实现](/rust/src/linkedlist/is_palindrome.rs)
+
+回文链表是一个特殊类型的链表,其中链表的前半部分和后半部分在反转后是相同的。也就是说,从前向后读和从后向前读,链表中的元素顺序是相同的。
+
+一种实现是可以遍历链表,将链表元素加入一个栈结构中,然后再将栈中元素和原始链表元素挨个比较,看是否相同
+(栈结构也可以采用后序递归遍历链表节点模拟,在递归调用中进行比较元素)。 这种方法唯一的缺点是空间复杂度为O(N),较高。
+
+另一种更优的空间复杂度实现方式是,找到链表中点,把中点之后的部分反转,再和中点之前的部分进行比较,看元素是否相等。空间复杂度为O(
+1)。
+
+## 数组相关
+
+### 双指针技巧
+
+双指针技巧主要分为两类:左右指针和快慢指针。
+
+#### 删除有序数组中的重复项
+
+Ref:
+
+[Go实现](/go/arraylist/remove_duplicates.go)、[Rust实现](/rust/src/arraylist/remove_duplicates.rs)
+
+由于数组是有序的,重复的元素必然是连续的,这很容易就能找到重复的元素。
+难点主要有两个:一是需要原地删除,不能用新数组;由此引出第二个难点,如果每次发现相同元素就地删除,这样的时间复杂度非常高(N^2),
+因为每删除一个元素就需要将后面的元素往前移动。
+
+高效实现此算法是通过快慢指针技巧:让慢指针 `slow` 走在后面,快指针 `fast` 走在前面探路,找到一个不重复的元素就让 `slow` 前进一步并把元素赋值给 `slow` 。
+这样,就保证了 `nums[0..slow]` 都是无重复的元素,当 `fast` 指针遍历完整个数组 `nums` 后,`nums[0..slow]` 就是整个数组去重之后的结果。
+
+#### 从数组中删除指定元素
+
+Ref:
+
+[Go实现](/go/arraylist/remove_element.go)、[Rust实现](/rust/src/arraylist/remove_element.rs)
+
+算法和上面类似,还是使用快慢指针。差别是快指针的值不等于指定值时,将值赋值给慢指针,再将慢指针往后走一步。(赋值前可以进一步判断快慢指针是否相同,相同则不用赋值,进一步提高性能)
+
+#### 移动数组中的零到末尾
+
+Ref:
+
+[Go实现](/go/arraylist/move_zeroes.go)、[Rust实现](/rust/src/arraylist/move_zeroes.rs)
+
+由于不能改变非零元素的相对顺序,基本上就只能从前往后顺序遍历,加上末尾都会是零,也就是前面的零都会被删除,因此可以基于上面的算法基础上,把零删除,然后再把尾部全部设置为0,即可。
+
+#### 反转字符串
+
+Ref:
+
+[Go实现](/go/arraylist/reverse_string.go)、[Rust实现](/rust/src/arraylist/reverse_string.rs)
+
+反转字符串实际上非常简单,在数组前后使用双指针,交换指针的值即可。
+
+#### 寻找最长回文子串
+
+Ref:
+
+[Go实现](/go/arraylist/longest_palindrome.go)、[Rust实现](/rust/src/arraylist/longest_palindrome.rs)
+
+实现思路是挨个从每个元素出发判断,寻找最长回文,找出最长的;但回文有两种形式需要考虑到,即中间位置是一个相同的元素和两个相同的元素
+(也可认为是奇数个回文和偶数个回文),因此每次判断是需要把两种情况都考虑进去。
+
+## 参考资料
+
+- [labuladong 的算法小抄](https://labuladong.github.io/algo/)
diff --git a/c/arraylist/arraylist.c b/c/arraylist/arraylist.c
deleted file mode 100644
index 990888b..0000000
--- a/c/arraylist/arraylist.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include
-#include
-#include
-#include "arraylist.h"
-
-#define ARRAYLIST_DEAUFALT_SIZE 4
-
-/**
- * create a new empty arraylist
- */
-ArrayList *listCreate() {
- ArrayList *list = malloc(sizeof(ArrayList));
-
- list->dataStore = malloc(sizeof(void *) * ARRAYLIST_DEAUFALT_SIZE);
- assert(list->dataStore);
- list->theSize = 0;
- list->capacity = ARRAYLIST_DEAUFALT_SIZE;
-
- list->size = listSize;
- list->get = listGet;
- list->set = listSet;
- list->append = listAppend;
- list->insert = listInsert;
- list->remove = listRemove;
- list->clear = listClear;
- list->destory = listDestory;
-
- return list;
-}
-
-void listIsRealloc(ArrayList *list) {
- if (list->theSize == list->capacity) {
- unsigned int newCapa = list->capacity * 2;
- list->capacity = newCapa;
- list->dataStore = realloc(list->dataStore, sizeof(void *) * newCapa);
- assert(list->dataStore);
- }
-}
-
-int listSize(ArrayList *list) {
- return list->theSize;
-}
-
-void *listGet(ArrayList *list, unsigned int index) {
- assert(index < list->theSize);
- return list->dataStore[index];
-}
-
-void listSet(ArrayList *list, unsigned int index, void *value) {
- assert(index < list->theSize);
- list->dataStore[index] = value;
-}
-
-void listAppend(ArrayList *list, void *value) {
- listIsRealloc(list);
- list->dataStore[list->theSize++] = value;
-}
-
-void listInsert(ArrayList *list, unsigned int index, void *value) {
- assert(index < list->theSize);
- listIsRealloc(list);
-
- memmove(list->dataStore+index+1, list->dataStore+index,
- sizeof(void *) * (list->theSize-index));
-
- list->dataStore[index] = value;
- list->theSize++;
-}
-
-void listRemove(ArrayList *list, unsigned int index) {
- assert(index < list->theSize);
- memmove(list->dataStore+index, list->dataStore+index+1,
- sizeof(void *) * (list->theSize-index-1));
-
- list->theSize--;
-}
-
-void listClear(ArrayList *list) {
- free(list->dataStore);
- list->dataStore = malloc(sizeof(void *) * ARRAYLIST_DEAUFALT_SIZE);
- assert(list->dataStore);
- list->theSize = 0;
- list->capacity = ARRAYLIST_DEAUFALT_SIZE;
-}
-
-void listDestory(ArrayList *list) {
- free(list->dataStore);
- free(list);
-}
diff --git a/c/arraylist/arraylist.h b/c/arraylist/arraylist.h
deleted file mode 100644
index b135812..0000000
--- a/c/arraylist/arraylist.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef __ARRAYLIST_H__
-#define __ARRAYLIST_H__
-
-typedef struct ArrayList {
- int theSize;
- int capacity;
- void **dataStore;
-
- int (*size)(struct ArrayList *);
- void *(*get)(struct ArrayList *, unsigned int);
- void (*set)(struct ArrayList *, unsigned int, void *);
- void (*append)(struct ArrayList *, void *);
- void (*insert)(struct ArrayList *, unsigned int, void *);
- void (*remove)(struct ArrayList *, unsigned int);
- void (*clear)(struct ArrayList *);
- void (*destory)(struct ArrayList *);
-} ArrayList;
-
-/* Prototypes */
-ArrayList *listCreate();
-int listSize(ArrayList *list);
-void *listGet(ArrayList *list, unsigned int index);
-void listSet(ArrayList *list, unsigned int index, void *value);
-void listAppend(ArrayList *list, void *value);
-void listInsert(ArrayList *list, unsigned int index, void *value);
-void listRemove(ArrayList *list, unsigned int index);
-void listClear(ArrayList *list);
-void listDestory(ArrayList *list);
-
-#endif /* __ARRAYLIST_H__ */
diff --git a/c/arraylist/arraylist_test.c b/c/arraylist/arraylist_test.c
deleted file mode 100644
index c0592b6..0000000
--- a/c/arraylist/arraylist_test.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include
-#include
-#include "arraylist.c"
-
-int main() {
- printf("\nRunning arraylist.c tests.\n\n");
-
- ArrayList *list = listCreate();
- assert(list->size(list) == 0);
-
- int a = 1;
- list->append(list, &a);
- assert(list->size(list) == 1);
-
- int item1 = *(int *)list->get(list, 0);
- assert(item1 == 1);
-
- char c = 'c';
- list->append(list, &c);
- assert(list->size(list) == 2);
-
- char item2 = *(char *)list->get(list, 1);
- assert(item2 == 'c');
-
- int b = 2;
- list->set(list, 1, &b);
- assert(list->size(list) == 2);
-
- int item2_new = *(int *)list->get(list, 1);
- assert(item2_new == 2);
-
- list->destory(list);
- list = NULL;
- assert(!list);
-
- int a1 = 1;
- int a2 = 2;
- int a3 = 3;
- int a4 = 4;
- ArrayList *list2 = listCreate();
- list2->append(list2, &a1);
- list2->append(list2, &a2);
- list2->append(list2, &a3);
- list2->insert(list2, 2, &a4);
- assert(list2->size(list2) == 4);
-
- int r1 = *(int *)list2->get(list2, 0);
- int r2 = *(int *)list2->get(list2, 1);
- int r3 = *(int *)list2->get(list2, 2);
- int r4 = *(int *)list2->get(list2, 3);
- assert(r1 == 1);
- assert(r2 == 2);
- assert(r3 == 4);
- assert(r4 == 3);
-
- list2->remove(list2, 0);
- r1 = *(int *)list2->get(list2, 0);
- assert(r1 == 2);
- assert(list2->size(list2) == 3);
-
- list2->clear(list2);
- assert(list2->size(list2) == 0);
-
- list2->destory(list2);
- list2 = NULL;
- assert(!list2);
-
- printf("All arraylist.c tests completed.\n\n");
- return 0;
-}
diff --git a/c/queue/queue.c b/c/queue/queue.c
deleted file mode 100644
index a264e5a..0000000
--- a/c/queue/queue.c
+++ /dev/null
@@ -1,75 +0,0 @@
-#include
-#include
-#include
-#include
-#include "queue.h"
-
-#define DEFAULT_QUEUE_SIZE 4
-
-Queue *queueCreate() {
- Queue *q = malloc(sizeof(Queue));
- assert(q);
- q->theSize = 0;
- q->capacity = DEFAULT_QUEUE_SIZE;
- q->dataStore = malloc(sizeof(void *) * DEFAULT_QUEUE_SIZE);
-
- q->enqueue = queueEnqueue;
- q->dequeue = queueDequeue;
- q->size = queueSize;
- q->isEmpty = queueIsEmpty;
- q->font = queueFont;
- q->end = queueEnd;
- q->destory = queueDestory;
-}
-
-void queueShouldRealloc(Queue *q) {
- if (q->theSize == q->capacity) {
- q->capacity = q->capacity * 2;
- q->dataStore = realloc(q->dataStore, sizeof(void *) * q->capacity);
- }
-}
-
-void queueEnqueue(Queue *q, void *element) {
- queueShouldRealloc(q);
- q->dataStore[q->theSize++] = element;
-}
-
-void* queueDequeue(Queue *q) {
- if (q->theSize == 0) return NULL;
- void *d = q->dataStore[0];
- if (q->theSize > 1) {
- int i;
- for (i = 0; i < q->theSize-1; i++) {
- q->dataStore[i] = q->dataStore[i+1];
- }
- }
- q->theSize--;
- return d;
-}
-
-int queueSize(Queue *q) {
- return q->theSize;
-}
-
-bool queueIsEmpty(Queue *q) {
- return q->theSize == 0;
-}
-
-void *queueFont(Queue *q) {
- if (q->theSize == 0) return NULL;
- return q->dataStore[0];
-}
-
-void *queueEnd(Queue *q) {
- if (q->theSize == 0) return NULL;
- return q->dataStore[q->theSize-1];
-}
-
-void queueClear(Queue *q) {
- q->theSize = 0;
-}
-
-void queueDestory(Queue *q) {
- free(q->dataStore);
- free(q);
-}
diff --git a/c/queue/queue.h b/c/queue/queue.h
deleted file mode 100644
index 75b3322..0000000
--- a/c/queue/queue.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef __QUEUE_H__
-#define __QUEUE_H__
-
-typedef struct Queue {
- int theSize;
- int capacity;
- void **dataStore;
-
- void (*enqueue)();
- void *(*dequeue)();
- int (*size)();
- bool (*isEmpty)();
- void *(*font)();
- void *(*end)();
- void (*clear)();
- void (*destory)();
-} Queue;
-
-/* Prototypes */
-Queue *queueCreate();
-void queueEnqueue(Queue *, void *);
-void *queueDequeue(Queue *);
-int queueSize(Queue *);
-bool queueIsEmpty(Queue *);
-void *queueFont(Queue *);
-void *queueEnd(Queue *);
-void queueClear(Queue *);
-void queueDestory(Queue *);
-
-#endif /*__QUEUE_H__*/
diff --git a/c/queue/queue_test.c b/c/queue/queue_test.c
deleted file mode 100644
index c2fb090..0000000
--- a/c/queue/queue_test.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#include
-#include
-#include "queue.c"
-
-int main() {
- printf("\nRunning queue.c tests.\n\n");
-
- Queue *q = queueCreate();
- assert(q->isEmpty(q));
- assert(q->size(q) == 0);
-
- int n1 = 1, n2 = 2, n3 = 3;
- int *p1 = &n1, *p2 = &n2, *p3 = &n3;
- q->enqueue(q, p1);
- q->enqueue(q, p2);
- q->enqueue(q, p3);
- assert(q->size(q) == 3);
-
- int d = *(int*)(q->dequeue(q));
- assert(q->size(q) == 2);
- assert(d == 1);
-
- int d2 = *(int*)q->font(q);
- int d3 = *(int*)q->end(q);
- assert(d2 == 2);
- assert(d3 == 3);
-
- q->destory(q);
-
- printf("All queue.c tests completed.\n\n");
- return 0;
-}
diff --git a/c/stack/stack.c b/c/stack/stack.c
deleted file mode 100644
index 81fd9f3..0000000
--- a/c/stack/stack.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#include
-#include
-#include
-#include
-#include "stack.h"
-
-#define DEFAULT_STACK_SIZE 4
-
-/**
- * create a new empty stack
- */
-Stack *stackCreate() {
- Stack *stack = malloc(sizeof(Stack));
- assert(stack);
- stack->top = 0;
- stack->capacity = DEFAULT_STACK_SIZE;
- stack->dataStore = malloc(sizeof(void *)*DEFAULT_STACK_SIZE);
-
- stack->size = stackSize;
- stack->isEmpty = stackIsEmpty;
- stack->push = stackPush;
- stack->pop = stackPop;
- stack->peek = stackPeek;
- stack->clear = stackClear;
- stack->destory = stackDestory;
-}
-
-void stackShouldRealloc(Stack *s) {
- if (s->top == s->capacity) {
- s->capacity = s->capacity*2;
- s->dataStore = realloc(s->dataStore, sizeof(void *) * s->capacity);
- }
-}
-
-int stackSize(Stack *s) {
- return s->top;
-}
-
-bool stackIsEmpty(Stack *s) {
- return s->top == 0;
-}
-
-void stackPush(Stack *s, void *element) {
- stackShouldRealloc(s);
- s->dataStore[s->top++] = element;
-}
-
-void *stackPop(Stack *s) {
- if (s->isEmpty(s)) {
- return NULL;
- }
-
- return s->dataStore[--s->top];
-}
-
-void *stackPeek(Stack *s) {
- if (s->isEmpty(s)) {
- return NULL;
- }
-
- return s->dataStore[s->top-1];
-}
-
-void stackClear(Stack *s) {
- s->top = 0;
-}
-
-void stackDestory(Stack *s) {
- free(s->dataStore);
- free(s);
-}
diff --git a/c/stack/stack.h b/c/stack/stack.h
deleted file mode 100644
index 182d5e3..0000000
--- a/c/stack/stack.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef __STACK_H__
-#define __STACK_H__
-
-typedef struct Stack {
- int top;
- int capacity;
- void **dataStore;
-
- int (*size)();
- bool (*isEmpty)();
- void (*push)();
- void *(*pop)();
- void *(*peek)();
- void (*clear)();
- void (*destory)();
-} Stack;
-
-/* Prototypes */
-Stack *stackCreate();
-int stackSize(Stack *s);
-bool stackIsEmpty(Stack *s);
-void stackPush(Stack *s, void *);
-void *stackPop(Stack *s);
-void *stackPeek(Stack *s);
-void stackClear(Stack *s);
-void stackDestory(Stack *s);
-
-#endif /* __STACK_H__ */
diff --git a/c/stack/stack_test.c b/c/stack/stack_test.c
deleted file mode 100644
index 1096e9b..0000000
--- a/c/stack/stack_test.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#include
-#include
-#include "stack.c"
-
-int main() {
- printf("\nRunning stack.c tests.\n\n");
-
- Stack *s = stackCreate();
- assert(s->isEmpty(s));
- assert(s->size(s) == 0);
-
- char *c1 = "12";
- char *c2 = "23";
- char *c3 = "34";
- s->push(s, c1);
- s->push(s, c2);
- s->push(s, c3);
- assert(s->size(s) == 3);
-
- char *c4 = "34";
- assert(strcmp(c4, (char *)s->pop(s)) == 0);
- char *c5 = "23";
- assert(strcmp(c5, (char *)s->peek(s)) == 0);
-
- s->clear(s);
- assert(s->isEmpty(s));
-
- s->destory(s);
-
- printf("All stack.c tests completed.\n\n");
- return 0;
-}
diff --git a/go/arraylist/arraylist.go b/go/arraylist/arraylist.go
index 5b1317c..f237a2a 100644
--- a/go/arraylist/arraylist.go
+++ b/go/arraylist/arraylist.go
@@ -50,14 +50,14 @@ func (list *ArrayList) Size() int {
func (list *ArrayList) Get(index int) (interface{}, error) {
if index < 0 || index >= list.Size() {
- return nil, errors.New("Index out of range.")
+ return nil, errors.New("index out of range")
}
return list.dataStore[index], nil
}
func (list *ArrayList) Set(index int, newVal interface{}) error {
if index < 0 || index >= list.Size() {
- return errors.New("Index out of range.")
+ return errors.New("index out of range")
}
list.dataStore[index] = newVal
@@ -71,7 +71,7 @@ func (list *ArrayList) Append(val interface{}) {
func (list *ArrayList) Insert(index int, val interface{}) error {
if index < 0 || index >= list.Size() {
- return errors.New("Index out of range.")
+ return errors.New("index out of range")
}
list.ensureCapacity()
list.dataStore = list.dataStore[:list.Size()+1]
@@ -87,7 +87,7 @@ func (list *ArrayList) Insert(index int, val interface{}) error {
func (list *ArrayList) Remove(index int) error {
if index < 0 || index >= list.Size() {
- return errors.New("Index out of range.")
+ return errors.New("index out of range")
}
list.dataStore = append(list.dataStore[:index], list.dataStore[index+1:]...)
list.theSize--
@@ -124,7 +124,7 @@ func (it *arrayListIterator) HasNext() bool {
func (it *arrayListIterator) Next() (interface{}, error) {
if !it.HasNext() {
- return nil, errors.New("No Such Element.")
+ return nil, errors.New("no such element")
}
v, err := it.list.Get(it.current)
it.current++
@@ -133,5 +133,5 @@ func (it *arrayListIterator) Next() (interface{}, error) {
func (it *arrayListIterator) Remove() {
it.current--
- it.list.Remove(it.current)
+ _ = it.list.Remove(it.current)
}
diff --git a/go/arraylist/arraylist_test.go b/go/arraylist/arraylist_test.go
index 703b7b4..4d3eea4 100644
--- a/go/arraylist/arraylist_test.go
+++ b/go/arraylist/arraylist_test.go
@@ -21,7 +21,7 @@ func TestGet(t *testing.T) {
list.Append("world")
v, err := list.Get(1)
if sv, ok := v.(string); !ok || sv != "world" || err != nil {
- t.Errorf("arraylist#Get() failed. get for error value:%s, expected value:world.")
+ t.Errorf("arraylist#Get() failed. get for error value:%s, expected value:world.", sv)
}
}
diff --git a/go/arraylist/longest_palindrome.go b/go/arraylist/longest_palindrome.go
new file mode 100644
index 0000000..9eb111c
--- /dev/null
+++ b/go/arraylist/longest_palindrome.go
@@ -0,0 +1,31 @@
+package arraylist
+
+func LongestPalindrome(s string) string {
+ res := ""
+ for i := 0; i < len(s); i++ {
+ // 以s[i]为中心的最长回文子串
+ s1 := palindrome(s, i, i)
+ // 以s[i]和s[i+1]为中心的最长回文子串
+ s2 := palindrome(s, i, i+1)
+
+ // 取最长回文串
+ if len(s1) > len(res) {
+ res = s1
+ }
+ if len(s2) > len(res) {
+ res = s2
+ }
+ }
+
+ return res
+}
+
+func palindrome(s string, l, r int) string {
+ for l >= 0 && r < len(s) && s[l] == s[r] {
+ // 当前是回文,向两边展开,继续判断
+ l--
+ r++
+ }
+ // 返回 l, r之间的最长回文
+ return s[l+1 : r]
+}
diff --git a/go/arraylist/longest_palindrome_test.go b/go/arraylist/longest_palindrome_test.go
new file mode 100644
index 0000000..74f0bc3
--- /dev/null
+++ b/go/arraylist/longest_palindrome_test.go
@@ -0,0 +1,17 @@
+package arraylist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLongestPalindrome(t *testing.T) {
+ s1 := "babad"
+ res := LongestPalindrome(s1)
+ assert.Equal(t, "bab", res)
+
+ s2 := "cbbd"
+ res = LongestPalindrome(s2)
+ assert.Equal(t, "bb", res)
+}
diff --git a/go/arraylist/move_zeroes.go b/go/arraylist/move_zeroes.go
new file mode 100644
index 0000000..6564a50
--- /dev/null
+++ b/go/arraylist/move_zeroes.go
@@ -0,0 +1,9 @@
+package arraylist
+
+func MoveZeroes(nums []int) {
+ res := RemoveElement(nums, 0)
+ for res < len(nums) {
+ nums[res] = 0
+ res++
+ }
+}
diff --git a/go/arraylist/move_zeroes_test.go b/go/arraylist/move_zeroes_test.go
new file mode 100644
index 0000000..67ea662
--- /dev/null
+++ b/go/arraylist/move_zeroes_test.go
@@ -0,0 +1,13 @@
+package arraylist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMoveZeroes(t *testing.T) {
+ nums := []int{0, 1, 0, 3, 12}
+ MoveZeroes(nums)
+ assert.Equal(t, []int{1, 3, 12, 0, 0}, nums)
+}
diff --git a/go/arraylist/remove_duplicates.go b/go/arraylist/remove_duplicates.go
new file mode 100644
index 0000000..394a180
--- /dev/null
+++ b/go/arraylist/remove_duplicates.go
@@ -0,0 +1,18 @@
+package arraylist
+
+func RemoveDuplicates(nums []int) int {
+ if len(nums) == 0 {
+ return 0
+ }
+
+ slow, fast := 0, 0
+ for fast < len(nums) {
+ if nums[slow] != nums[fast] {
+ slow++
+ nums[slow] = nums[fast]
+ }
+ fast++
+ }
+
+ return slow + 1
+}
diff --git a/go/arraylist/remove_duplicates_test.go b/go/arraylist/remove_duplicates_test.go
new file mode 100644
index 0000000..582d9da
--- /dev/null
+++ b/go/arraylist/remove_duplicates_test.go
@@ -0,0 +1,17 @@
+package arraylist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_RemoveDuplicates(t *testing.T) {
+ var res1 = RemoveDuplicates(nil)
+ assert.Equal(t, 0, res1)
+
+ var arr = []int{1, 2, 2, 3, 3, 3, 4, 4, 4, 4}
+ var res2 = RemoveDuplicates(arr)
+ assert.Equal(t, 4, res2)
+ assert.Equal(t, []int{1, 2, 3, 4}, arr[0:res2])
+}
diff --git a/go/arraylist/remove_element.go b/go/arraylist/remove_element.go
new file mode 100644
index 0000000..4a810f2
--- /dev/null
+++ b/go/arraylist/remove_element.go
@@ -0,0 +1,17 @@
+package arraylist
+
+func RemoveElement(nums []int, val int) int {
+ slow, fast := 0, 0
+
+ for fast < len(nums) {
+ if nums[fast] != val {
+ if slow != fast {
+ nums[slow] = nums[fast]
+ }
+ slow++
+ }
+ fast++
+ }
+
+ return slow
+}
diff --git a/go/arraylist/remove_element_test.go b/go/arraylist/remove_element_test.go
new file mode 100644
index 0000000..9ad4ca5
--- /dev/null
+++ b/go/arraylist/remove_element_test.go
@@ -0,0 +1,17 @@
+package arraylist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRemoveElement(t *testing.T) {
+ nums := []int{3, 2, 2, 3}
+ assert.Equal(t, 4, RemoveElement(nums, 4))
+
+ val := 3
+ res := RemoveElement(nums, val)
+ assert.Equal(t, 2, res)
+ assert.Equal(t, []int{2, 2}, nums[:res])
+}
diff --git a/go/arraylist/reverse_string.go b/go/arraylist/reverse_string.go
new file mode 100644
index 0000000..b5af015
--- /dev/null
+++ b/go/arraylist/reverse_string.go
@@ -0,0 +1,10 @@
+package arraylist
+
+func ReverseString(s []byte) {
+ p1, p2 := 0, len(s)-1
+ for p1 < p2 {
+ s[p1], s[p2] = s[p2], s[p1]
+ p1++
+ p2--
+ }
+}
diff --git a/go/arraylist/reverse_string_test.go b/go/arraylist/reverse_string_test.go
new file mode 100644
index 0000000..27ad28d
--- /dev/null
+++ b/go/arraylist/reverse_string_test.go
@@ -0,0 +1,13 @@
+package arraylist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestReverseString(t *testing.T) {
+ s := []byte("hello world")
+ ReverseString(s)
+ assert.Equal(t, []byte("dlrow olleh"), s)
+}
diff --git a/go/go.mod b/go/go.mod
new file mode 100644
index 0000000..68c4699
--- /dev/null
+++ b/go/go.mod
@@ -0,0 +1,14 @@
+module the_algorithms
+
+go 1.20
+
+require (
+ github.com/mitchellh/hashstructure/v2 v2.0.2
+ github.com/stretchr/testify v1.8.4
+)
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go/go.sum b/go/go.sum
new file mode 100644
index 0000000..c68149c
--- /dev/null
+++ b/go/go.sum
@@ -0,0 +1,12 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
+github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/go/hashmap/hashmap.go b/go/hashmap/hashmap.go
index 0a19b87..34376dd 100644
--- a/go/hashmap/hashmap.go
+++ b/go/hashmap/hashmap.go
@@ -3,7 +3,7 @@ package hashmap
import (
"fmt"
- "github.com/mitchellh/hashstructure"
+ "github.com/mitchellh/hashstructure/v2"
)
const ratio = .75 // ratio sets the capacity the hashmap has to be at before it expands
@@ -32,7 +32,7 @@ func hash(key uint64) uint64 {
}
func hashAny(data interface{}) int64 {
- h, _ := hashstructure.Hash(data, nil)
+ h, _ := hashstructure.Hash(data, hashstructure.FormatV2, nil)
return int64(hash(h))
}
diff --git a/go/linkedlist/detect_cycle.go b/go/linkedlist/detect_cycle.go
new file mode 100644
index 0000000..3352b46
--- /dev/null
+++ b/go/linkedlist/detect_cycle.go
@@ -0,0 +1,25 @@
+package linkedlist
+
+func DetectCycle(head *ListNode) *ListNode {
+ slow := head
+ fast := head
+
+ for fast != nil && fast.Next != nil {
+ slow = slow.Next
+ fast = fast.Next.Next
+ if slow == fast {
+ break
+ }
+ }
+ if fast == nil || fast.Next == nil {
+ return nil
+ }
+
+ slow = head
+ for slow != fast {
+ slow = slow.Next
+ fast = fast.Next
+ }
+
+ return slow
+}
diff --git a/go/linkedlist/detect_cycle_test.go b/go/linkedlist/detect_cycle_test.go
new file mode 100644
index 0000000..7cb3353
--- /dev/null
+++ b/go/linkedlist/detect_cycle_test.go
@@ -0,0 +1,37 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_DetectCycle(t *testing.T) {
+ ret1 := DetectCycle(nil)
+ assert.Nil(t, ret1)
+
+ ret2 := DetectCycle(&ListNode{
+ Val: 1,
+ Next: &ListNode{},
+ })
+ assert.Nil(t, ret2)
+
+ head := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ },
+ }
+ node1 := &ListNode{
+ Val: 3,
+ }
+ node2 := &ListNode{
+ Val: 4,
+ }
+ head.Next = node1
+ node1.Next = node2
+ node2.Next = node1
+
+ ret3 := DetectCycle(head)
+ assert.Equal(t, ret3.Val, 3)
+}
diff --git a/go/linkedlist/get_intersection_node.go b/go/linkedlist/get_intersection_node.go
new file mode 100644
index 0000000..a16655d
--- /dev/null
+++ b/go/linkedlist/get_intersection_node.go
@@ -0,0 +1,39 @@
+package linkedlist
+
+func GetIntersectionNode(headA, headB *ListNode) *ListNode {
+ p1 := headA
+ p2 := headB
+ lenA := 0
+ lenB := 0
+
+ for p1 != nil {
+ p1 = p1.Next
+ lenA++
+ }
+ for p2 != nil {
+ p2 = p2.Next
+ lenB++
+ }
+ if lenA == 0 || lenB == 0 {
+ return nil
+ }
+
+ p1 = headA
+ p2 = headB
+ if lenA >= lenB { // A 比 B 长,则A先走 lenA - lenB 步
+ for i := 0; i < lenA-lenB; i++ {
+ p1 = p1.Next
+ }
+ } else { // B 比 A 长,则B先走 lenB - lenA 步
+ for i := 0; i < lenB-lenA; i++ {
+ p2 = p2.Next
+ }
+ }
+ // 再一起往后走,直到相遇
+ for p1 != p2 {
+ p1 = p1.Next
+ p2 = p2.Next
+ }
+
+ return p1
+}
diff --git a/go/linkedlist/get_intersection_node_test.go b/go/linkedlist/get_intersection_node_test.go
new file mode 100644
index 0000000..b0e4e5f
--- /dev/null
+++ b/go/linkedlist/get_intersection_node_test.go
@@ -0,0 +1,26 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_GetIntersectionNode(t *testing.T) {
+ ret1 := GetIntersectionNode(nil, nil)
+ assert.Nil(t, ret1)
+
+ head1 := &ListNode{Val: 1}
+ head2 := &ListNode{Val: 10}
+ node1 := &ListNode{Val: 2}
+ node2 := &ListNode{Val: 11}
+ nodeInte := &ListNode{Val: 3, Next: &ListNode{Val: 4}}
+
+ head1.Next = node1
+ node1.Next = nodeInte
+ head2.Next = node2
+ node2.Next = nodeInte
+
+ ret2 := GetIntersectionNode(head1, head2)
+ assert.Equal(t, ret2, nodeInte)
+}
diff --git a/go/linkedlist/is_palindrome.go b/go/linkedlist/is_palindrome.go
new file mode 100644
index 0000000..7dfe8eb
--- /dev/null
+++ b/go/linkedlist/is_palindrome.go
@@ -0,0 +1,84 @@
+package linkedlist
+
+// IsPalindromeV1 V1版本采用递归方式,代码简单,空间复杂度为O(N)
+func IsPalindromeV1(head *ListNode) bool {
+ if head == nil {
+ return false
+ }
+ if head.Next == nil {
+ return true
+ }
+
+ h := head
+
+ var traverse func(node *ListNode) bool
+ traverse = func(node *ListNode) bool {
+ if node == nil {
+ return true
+ }
+
+ eq := traverse(node.Next)
+ eq = eq && h.Val == node.Val
+ h = h.Next
+
+ return eq
+ }
+
+ return traverse(head)
+}
+
+// IsPalindromeV2 不用递归,使空间复杂度变为O(1)
+func IsPalindromeV2(head *ListNode) bool {
+ if head == nil {
+ return false
+ }
+ if head.Next == nil {
+ return true
+ }
+
+ middle := findMiddle(head)
+ rev := reverseNode(middle)
+
+ h := head
+ for rev != nil {
+ if rev.Val != h.Val {
+ return false
+ }
+ rev = rev.Next
+ h = h.Next
+ }
+
+ return true
+}
+
+func findMiddle(head *ListNode) *ListNode {
+ if head == nil {
+ return nil
+ }
+
+ fast := head
+ slow := head
+ for fast != nil && fast.Next != nil {
+ fast = fast.Next.Next
+ slow = slow.Next
+ }
+
+ return slow
+}
+
+func reverseNode(node *ListNode) *ListNode {
+ if node == nil || node.Next == nil {
+ return node
+ }
+
+ prev := (*ListNode)(nil)
+ curr := node
+ for curr != nil {
+ next := curr.Next
+ curr.Next = prev
+ prev = curr
+ curr = next
+ }
+
+ return prev
+}
diff --git a/go/linkedlist/is_palindrome_test.go b/go/linkedlist/is_palindrome_test.go
new file mode 100644
index 0000000..bc1b065
--- /dev/null
+++ b/go/linkedlist/is_palindrome_test.go
@@ -0,0 +1,66 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_IsPalindrome(t *testing.T) {
+ fns := []func(node *ListNode) bool{IsPalindromeV1, IsPalindromeV2}
+ for _, fn := range fns {
+ ret1 := fn(nil)
+ assert.False(t, ret1)
+
+ ret2 := fn(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 1,
+ Next: nil,
+ },
+ },
+ })
+ assert.True(t, ret2)
+
+ ret3 := fn(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 3,
+ Next: nil,
+ },
+ },
+ },
+ })
+ assert.False(t, ret3)
+
+ ret4 := fn(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 1,
+ Next: nil,
+ },
+ },
+ },
+ })
+ assert.True(t, ret4)
+
+ ret5 := fn(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: nil,
+ },
+ })
+ assert.False(t, ret5)
+ }
+}
diff --git a/go/linkedlist/linkedlist.go b/go/linkedlist/linkedlist.go
index 036ceb4..60c7b04 100644
--- a/go/linkedlist/linkedlist.go
+++ b/go/linkedlist/linkedlist.go
@@ -1,159 +1,7 @@
package linkedlist
-type Lister interface {
- Clear()
- Len() int
- Front() *Element
- Back() *Element
- Remove(e *Element) interface{}
- PushFront(v interface{}) *Element
- PushBack(v interface{}) *Element
- InsertBefore(v interface{}, mark *Element) *Element
- InsertAfter(v interface{}, mark *Element) *Element
- MoveBefore(e, mark *Element)
- MoveAfter(e, mark *Element)
- MoveToFront(e *Element)
-}
-
-type Element struct {
- prev, next *Element
- list *List
- Value interface{}
-}
-
-func (e *Element) Next() *Element {
- if n := e.next; n != nil && n != &e.list.root {
- return n
- }
- return nil
-}
-
-func (e *Element) Prev() *Element {
- if p := e.prev; p != nil && p != &e.list.root {
- return p
- }
- return nil
-}
-
-type List struct {
- root Element
- len int
-}
-
-func New() *List {
- l := new(List)
- l.Clear()
- return l
-}
-
-func (l *List) Clear() {
- if firstElem := l.root.next; firstElem != nil && firstElem.list == l {
- firstElem.prev = nil
- }
- if lastElem := l.root.prev; lastElem != nil && lastElem.list == l {
- lastElem.next = nil
- }
- l.root.next = &l.root
- l.root.prev = &l.root
- l.len = 0
-}
-
-func (l *List) Len() int {
- return l.len
-}
-
-func (l *List) Front() *Element {
- if l.len == 0 {
- return nil
- }
- return l.root.next
-}
-
-func (l *List) Back() *Element {
- if l.len == 0 {
- return nil
- }
- return l.root.prev
-}
-
-func (l *List) remove(e *Element) *Element {
- e.prev.next = e.next
- e.next.prev = e.prev
- e.prev = nil
- e.next = nil
- e.list = nil
- l.len--
- return e
-}
-
-func (l *List) Remove(e *Element) interface{} {
- if e.list == l {
- l.remove(e)
- }
- return e.Value
-}
-
-func (l *List) insert(e, at *Element) *Element {
- n := at.next
- at.next = e
- e.prev = at
- e.next = n
- n.prev = e
- e.list = l
- l.len++
- return e
-}
-
-func (l *List) insertValue(v interface{}, at *Element) *Element {
- return l.insert(&Element{Value: v}, at)
-}
-
-func (l *List) PushFront(v interface{}) *Element {
- return l.insertValue(v, &l.root)
-}
-
-func (l *List) PushBack(v interface{}) *Element {
- return l.insertValue(v, l.root.prev)
-}
-
-func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
- if mark.list != l {
- return nil
- }
- return l.insertValue(v, mark.prev)
-}
-
-func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
- if mark.list != l {
- return nil
- }
- return l.insertValue(v, mark)
-}
-
-func (l *List) MoveBefore(e, mark *Element) {
- if e.list != l || mark.list != l || e == mark {
- return
- }
- l.insert(l.remove(e), mark.prev)
-}
-
-func (l *List) MoveAfter(e, mark *Element) {
- if e.list != l || mark.list != l || e == mark {
- return
- }
- l.insert(l.remove(e), mark)
-}
-
-func (l *List) MoveToFront(e *Element) {
- if e.list != l || l.root.next == e {
- return
- }
- l.insert(l.remove(e), &l.root)
-}
-
-func (l *List) MoveToBack(e *Element) {
- if e.list != l || l.root.prev == e {
- return
- }
- l.insert(l.remove(e), l.root.prev)
+// Definition for singly-linked list.
+type ListNode struct {
+ Val int
+ Next *ListNode
}
diff --git a/go/linkedlist/linkedlist_test.go b/go/linkedlist/linkedlist_test.go
deleted file mode 100644
index 4e8209a..0000000
--- a/go/linkedlist/linkedlist_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package linkedlist
-
-import "testing"
-
-func checkListLen(t *testing.T, l *List, len int) bool {
- if n := l.Len(); n != len {
- t.Errorf("l.Len() = %d, want %d\n", n, len)
- return false
- }
- return true
-}
-
-func checkListPointers(t *testing.T, l *List, es []*Element) {
- root := &l.root
-
- if !checkListLen(t, l, len(es)) {
- return
- }
-
- if len(es) == 0 {
- if l.root.next != root || l.root.prev != root {
- t.Errorf("l.root.next = %p, l.root.prev = %p; both should be %p\n",
- l.root.next, l.root.prev, root)
- }
- return
- }
-
- for i, e := range es {
- prev := root
- Prev := (*Element)(nil)
- if i > 0 {
- prev = es[i-1]
- Prev = prev
- }
- if p := e.prev; p != prev {
- t.Errorf("es[%d](%p).prev = %p, want %p", i, e.prev, prev)
- }
- if p := e.Prev(); p != Prev {
- t.Errorf("es[%d](%p).Prev() = %p, want %p", i, e.Prev(), prev)
- }
-
- next := root
- Next := (*Element)(nil)
- if i < len(es)-1 {
- next = es[i+1]
- Next = next
- }
- if n := e.next; n != next {
- t.Errorf("es[%d](%p).next = %p, want %p", i, e, n, next)
- }
- if n := e.Next(); n != Next {
- t.Errorf("es[%d](%p).Next() = %p, want %p", i, e, n, Next)
- }
- }
-}
-
-func TestList(t *testing.T) {
- l := New()
- checkListPointers(t, l, []*Element{})
-
- // Single element list
- e := l.PushFront("a")
- checkListPointers(t, l, []*Element{e})
- l.MoveToFront(e)
- checkListPointers(t, l, []*Element{e})
- l.MoveToBack(e)
- checkListPointers(t, l, []*Element{e})
- l.Remove(e)
- checkListPointers(t, l, []*Element{})
-
- // Bigger list
- e2 := l.PushFront(2)
- e1 := l.PushFront(1)
- e3 := l.PushBack(3)
- e4 := l.PushBack("banana")
- checkListPointers(t, l, []*Element{e1, e2, e3, e4})
-
- l.Remove(e2)
- checkListPointers(t, l, []*Element{e1, e3, e4})
-
- l.MoveToFront(e3) // move from middle
- checkListPointers(t, l, []*Element{e3, e1, e4})
-
- l.MoveToFront(e1)
- l.MoveToBack(e3) // move from middle
- checkListPointers(t, l, []*Element{e1, e4, e3})
-
- l.MoveToFront(e3) // move from back
- checkListPointers(t, l, []*Element{e3, e1, e4})
- l.MoveToFront(e3) // should be no-op
- checkListPointers(t, l, []*Element{e3, e1, e4})
-
- l.MoveToBack(e3) // move from front
- checkListPointers(t, l, []*Element{e1, e4, e3})
- l.MoveToBack(e3) // should be no-op
- checkListPointers(t, l, []*Element{e1, e4, e3})
-
- e2 = l.InsertBefore(2, e1) // insert before front
- checkListPointers(t, l, []*Element{e2, e1, e4, e3})
- l.Remove(e2)
- e2 = l.InsertBefore(2, e4) // insert before middle
- checkListPointers(t, l, []*Element{e1, e2, e4, e3})
- l.Remove(e2)
- e2 = l.InsertBefore(2, e3) // insert before back
- checkListPointers(t, l, []*Element{e1, e4, e2, e3})
- l.Remove(e2)
-
- e2 = l.InsertAfter(2, e1) // insert after front
- checkListPointers(t, l, []*Element{e1, e2, e4, e3})
- l.Remove(e2)
- e2 = l.InsertAfter(2, e4) // insert after middle
- checkListPointers(t, l, []*Element{e1, e4, e2, e3})
- l.Remove(e2)
- e2 = l.InsertAfter(2, e3) // insert after back
- checkListPointers(t, l, []*Element{e1, e4, e3, e2})
- l.Remove(e2)
-
- // Check standard iteration.
- sum := 0
- for e := l.Front(); e != nil; e = e.Next() {
- if i, ok := e.Value.(int); ok {
- sum += i
- }
- }
- if sum != 4 {
- t.Errorf("sum over l = %d, want 4", sum)
- }
-
- // Clear all elements by iterating
- var next *Element
- for e := l.Front(); e != nil; e = next {
- next = e.Next()
- l.Remove(e)
- }
- checkListPointers(t, l, []*Element{})
-
- l.PushBack(1)
- l.PushBack(2)
- l.PushBack(3)
- l.Clear()
- checkListPointers(t, l, []*Element{})
-}
diff --git a/go/linkedlist/merge_k_lists.go b/go/linkedlist/merge_k_lists.go
new file mode 100644
index 0000000..c28af6f
--- /dev/null
+++ b/go/linkedlist/merge_k_lists.go
@@ -0,0 +1,59 @@
+package linkedlist
+
+import "container/heap"
+
+func MergeKLists(lists []*ListNode) *ListNode {
+ // 虚拟头结点
+ dummy := &ListNode{}
+ // 指向虚拟头节点最后的节点,用于设置新节点
+ p := dummy
+
+ pq := make(PriorityQueue, 0)
+ heap.Init(&pq)
+ for _, head := range lists {
+ // 将K个链表头节点加入优先级队列
+ if head != nil {
+ heap.Push(&pq, head)
+ }
+ }
+
+ // 从优先级队列中逐个取出节点加入新链表中,
+ // 并把新的链表头节点加入优先级队列中
+ for pq.Len() > 0 {
+ node := heap.Pop(&pq).(*ListNode)
+ p.Next = node
+ if node.Next != nil {
+ heap.Push(&pq, node.Next)
+ }
+ p = p.Next
+ }
+
+ return dummy.Next
+}
+
+type PriorityQueue []*ListNode
+
+func (pq PriorityQueue) Len() int { return len(pq) }
+
+func (pq PriorityQueue) Less(i, j int) bool {
+ // We want Pop to give us the highest, not lowest, priority so we use greater than here.
+ return pq[i].Val < pq[j].Val
+}
+
+func (pq PriorityQueue) Swap(i, j int) {
+ pq[i], pq[j] = pq[j], pq[i]
+}
+
+func (pq *PriorityQueue) Push(x any) {
+ item := x.(*ListNode)
+ *pq = append(*pq, item)
+}
+
+func (pq *PriorityQueue) Pop() any {
+ old := *pq
+ n := len(old)
+ item := old[n-1]
+ old[n-1] = nil // avoid memory leak
+ *pq = old[0 : n-1]
+ return item
+}
diff --git a/go/linkedlist/merge_k_lists_test.go b/go/linkedlist/merge_k_lists_test.go
new file mode 100644
index 0000000..e0703d4
--- /dev/null
+++ b/go/linkedlist/merge_k_lists_test.go
@@ -0,0 +1,66 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_MergeKLists(t *testing.T) {
+ ret1 := MergeKLists(nil)
+ assert.Nil(t, ret1)
+ ret2 := MergeKLists([]*ListNode{})
+ assert.Nil(t, ret2)
+
+ arrList := []*ListNode{
+ {
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ },
+ {
+ Val: 1,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 4,
+ },
+ },
+ },
+ {
+ Val: 2,
+ Next: &ListNode{
+ Val: 6,
+ },
+ },
+ }
+ ret3 := MergeKLists(arrList)
+ assert.Equal(t, ret3, &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ Next: &ListNode{
+ Val: 6,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+}
diff --git a/go/linkedlist/merge_two_lists.go b/go/linkedlist/merge_two_lists.go
new file mode 100644
index 0000000..1ca470a
--- /dev/null
+++ b/go/linkedlist/merge_two_lists.go
@@ -0,0 +1,61 @@
+package linkedlist
+
+func MergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
+ if list1 == nil {
+ return list2
+ }
+ if list2 == nil {
+ return list1
+ }
+
+ var p1 = list1
+ var p2 = list2
+
+ var dummy = &ListNode{}
+ var p = dummy
+
+ for {
+ if p1.Val <= p2.Val {
+ p.Next = p1
+ p1 = p1.Next
+ } else {
+ p.Next = p2
+ p2 = p2.Next
+ }
+ p = p.Next
+
+ if p1 == nil || p2 == nil {
+ break
+ }
+ }
+
+ if p1 != nil {
+ p.Next = p1
+ }
+ if p2 != nil {
+ p.Next = p2
+ }
+
+ return dummy.Next
+}
+
+func MergeTwoListsWithRecursion(list1 *ListNode, list2 *ListNode) *ListNode {
+ if list1 == nil {
+ return list2
+ }
+ if list2 == nil {
+ return list1
+ }
+
+ if list1.Val <= list2.Val {
+ return &ListNode{
+ Val: list1.Val,
+ Next: MergeTwoListsWithRecursion(list1.Next, list2),
+ }
+ } else {
+ return &ListNode{
+ Val: list2.Val,
+ Next: MergeTwoListsWithRecursion(list1, list2.Next),
+ }
+ }
+}
diff --git a/go/linkedlist/merge_two_lists_test.go b/go/linkedlist/merge_two_lists_test.go
new file mode 100644
index 0000000..508eb0b
--- /dev/null
+++ b/go/linkedlist/merge_two_lists_test.go
@@ -0,0 +1,57 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_MergeTwoLists(t *testing.T) {
+ var fnList = []func(list1 *ListNode, list2 *ListNode) *ListNode{
+ MergeTwoLists, MergeTwoListsWithRecursion,
+ }
+
+ for _, fn := range fnList {
+ l1 := fn(nil, nil)
+ assert.Nil(t, l1)
+
+ l2 := fn(nil, &ListNode{})
+ assert.Equal(t, l2, &ListNode{})
+
+ l3 := fn(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ }, &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 6,
+ },
+ },
+ })
+ assert.Equal(t, l3, &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ Next: &ListNode{
+ Val: 6,
+ },
+ },
+ },
+ },
+ },
+ })
+ }
+}
diff --git a/go/linkedlist/middle_node.go b/go/linkedlist/middle_node.go
new file mode 100644
index 0000000..eb4bbf4
--- /dev/null
+++ b/go/linkedlist/middle_node.go
@@ -0,0 +1,13 @@
+package linkedlist
+
+func MiddleNode(head *ListNode) *ListNode {
+ slow := head
+ fast := head
+
+ for fast != nil && fast.Next != nil {
+ slow = slow.Next
+ fast = fast.Next.Next
+ }
+
+ return slow
+}
diff --git a/go/linkedlist/middle_node_test.go b/go/linkedlist/middle_node_test.go
new file mode 100644
index 0000000..ea05a58
--- /dev/null
+++ b/go/linkedlist/middle_node_test.go
@@ -0,0 +1,44 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_MiddleNode(t *testing.T) {
+ ret1 := MiddleNode(nil)
+ assert.Nil(t, ret1)
+
+ ret2 := MiddleNode(&ListNode{
+ Val: 1,
+ Next: nil,
+ })
+ assert.Equal(t, ret2, &ListNode{Val: 1, Next: nil})
+
+ ret3 := MiddleNode(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: nil,
+ },
+ },
+ })
+ assert.Equal(t, ret3, &ListNode{Val: 2, Next: &ListNode{Val: 3, Next: nil}})
+
+ ret4 := MiddleNode(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 4,
+ },
+ },
+ },
+ })
+ assert.Equal(t, ret4, &ListNode{Val: 3, Next: &ListNode{Val: 4, Next: nil}})
+}
diff --git a/go/linkedlist/partition.go b/go/linkedlist/partition.go
new file mode 100644
index 0000000..f70891c
--- /dev/null
+++ b/go/linkedlist/partition.go
@@ -0,0 +1,29 @@
+package linkedlist
+
+func Partition(head *ListNode, x int) *ListNode {
+ // 左节点用于存储小于x的值, 右节点用于存储大于x的值
+ var dummyLeft, dummyRight = &ListNode{}, &ListNode{}
+ // p1, p2分别用生成新的左右链表节点
+ var p1, p2 = dummyLeft, dummyRight
+
+ // p指针用于遍历head链表
+ var p = head
+
+ for p != nil {
+ if p.Val < x {
+ p1.Next = p
+ p1 = p1.Next
+ } else {
+ p2.Next = p
+ p2 = p2.Next
+ }
+ // 断开新生成的p1,p2指针和原链表的连接关系
+ tmp := p.Next
+ p.Next = nil
+ p = tmp
+ }
+
+ p1.Next = dummyRight.Next
+
+ return dummyLeft.Next
+}
diff --git a/go/linkedlist/partition_test.go b/go/linkedlist/partition_test.go
new file mode 100644
index 0000000..cebfe68
--- /dev/null
+++ b/go/linkedlist/partition_test.go
@@ -0,0 +1,35 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Partition(t *testing.T) {
+ ret1 := Partition(nil, 0)
+ assert.Nil(t, ret1)
+
+ ret2 := Partition(&ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 0,
+ Next: &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: -1,
+ }},
+ }}, 1)
+ assert.Equal(t, ret2, &ListNode{
+ Val: 0,
+ Next: &ListNode{
+ Val: -1,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 1,
+ },
+ },
+ },
+ })
+}
diff --git a/go/linkedlist/remove_nth_from_end.go b/go/linkedlist/remove_nth_from_end.go
new file mode 100644
index 0000000..8dd016a
--- /dev/null
+++ b/go/linkedlist/remove_nth_from_end.go
@@ -0,0 +1,45 @@
+package linkedlist
+
+func RemoveNthFromEnd(head *ListNode, n int) *ListNode {
+ dummy := &ListNode{0, head}
+ first, second := dummy, dummy
+
+ // 第一步,让first节点先向前移动n+1步
+ for i := 0; i < n+1; i++ {
+ first = first.Next
+ if first == nil && i < n { // 链表长度小于n,不需要删除任何节点
+ return head
+ }
+ }
+
+ // 第二步,让first和second节点同时向前移动,直到first节点到达链表的末尾
+ for first != nil {
+ first = first.Next
+ second = second.Next
+ }
+
+ // 第三步,删除second节点的下一个节点
+ second.Next = second.Next.Next
+
+ return dummy.Next
+}
+
+type recursion struct {
+ // 记录递归栈退出的次数,用于和n比较
+ count int
+}
+
+func (r *recursion) RemoveNthFromEndWithRecursion(head *ListNode, n int) *ListNode {
+ if head == nil {
+ return nil
+ }
+
+ head.Next = r.RemoveNthFromEndWithRecursion(head.Next, n)
+
+ r.count++
+ if r.count == n {
+ return head.Next
+ }
+
+ return head
+}
diff --git a/go/linkedlist/remove_nth_from_end_test.go b/go/linkedlist/remove_nth_from_end_test.go
new file mode 100644
index 0000000..54423f5
--- /dev/null
+++ b/go/linkedlist/remove_nth_from_end_test.go
@@ -0,0 +1,104 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_RemoveNthFromEnd(t *testing.T) {
+ ret1 := RemoveNthFromEnd(nil, 1)
+ assert.Nil(t, ret1)
+
+ lists := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ }
+ ret2 := RemoveNthFromEnd(lists, 1)
+ assert.Equal(t, ret2, &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ },
+ })
+
+ lists2 := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ }
+ ret3 := RemoveNthFromEnd(lists2, 2)
+ assert.Equal(t, ret3, &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 5,
+ },
+ })
+
+}
+
+func Test_RemoveNthFromEndWithRecursion(t *testing.T) {
+ ret1 := (&recursion{}).RemoveNthFromEndWithRecursion(nil, 1)
+ assert.Nil(t, ret1)
+
+ lists := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ }
+ ret2 := (&recursion{}).RemoveNthFromEndWithRecursion(lists, 1)
+ assert.Equal(t, ret2, &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ },
+ })
+
+ lists2 := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ }
+ ret3 := (&recursion{}).RemoveNthFromEndWithRecursion(lists2, 2)
+ assert.Equal(t, ret3, &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 5,
+ },
+ })
+
+ lists3 := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ }
+ ret4 := (&recursion{}).RemoveNthFromEndWithRecursion(lists3, 3)
+ assert.Equal(t, ret4, &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ })
+
+}
diff --git a/go/linkedlist/reverse_k_group.go b/go/linkedlist/reverse_k_group.go
new file mode 100644
index 0000000..f8af05d
--- /dev/null
+++ b/go/linkedlist/reverse_k_group.go
@@ -0,0 +1,38 @@
+package linkedlist
+
+// 翻转[a, b)的元素
+func reverse(a, b *ListNode) *ListNode {
+ var pre, curr *ListNode = nil, a
+ // 移动到b指针时,停止
+ for curr != b {
+ next := curr.Next
+ curr.Next = pre
+ pre = curr
+ curr = next
+ }
+ // 返回翻转后的头结点
+ return pre
+}
+
+func ReverseKGroup(head *ListNode, k int) *ListNode {
+ if head == nil || head.Next == nil {
+ return head
+ }
+
+ // 找到K的翻转区间
+ a, b := head, head
+ for i := 0; i < k; i++ {
+ if b == nil { // 不足k个节点时,直接返回head
+ return head
+ }
+ b = b.Next
+ }
+
+ // 翻转前k个元素
+ newHead := reverse(a, b)
+
+ // 递归执行此操作
+ a.Next = ReverseKGroup(b, k)
+
+ return newHead
+}
diff --git a/go/linkedlist/reverse_k_group_test.go b/go/linkedlist/reverse_k_group_test.go
new file mode 100644
index 0000000..d239ed9
--- /dev/null
+++ b/go/linkedlist/reverse_k_group_test.go
@@ -0,0 +1,44 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_ReverseKGroup(t *testing.T) {
+ ret1 := ReverseKGroup(nil, 2)
+ assert.Nil(t, ret1)
+
+ list := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ },
+ },
+ }
+ ret2 := ReverseKGroup(list, 2)
+ assert.Equal(t, ret2, &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 5,
+ },
+ },
+ },
+ },
+ })
+}
diff --git a/go/linkedlist/reverse_list.go b/go/linkedlist/reverse_list.go
new file mode 100644
index 0000000..f58a028
--- /dev/null
+++ b/go/linkedlist/reverse_list.go
@@ -0,0 +1,18 @@
+package linkedlist
+
+func ReverseList(head *ListNode) *ListNode {
+ // 定义递归退出条件
+ if head == nil || head.Next == nil {
+ return head
+ }
+ // 递归返回最后一个节点作为头结点返回
+ last := ReverseList(head.Next)
+
+ // 从倒数第二个节点开始,对其后面的一个节点执行链表反转
+ head.Next.Next = head
+ // 从倒数第二个节点开始,将自身的Next指针递归指向Nil
+ //(中间节点其实不用这一步也可以,但是因为最后一个head节点必须要这样做,因此统一做最好)
+ head.Next = nil
+
+ return last
+}
diff --git a/go/linkedlist/reverse_list_n.go b/go/linkedlist/reverse_list_n.go
new file mode 100644
index 0000000..3f3e1c8
--- /dev/null
+++ b/go/linkedlist/reverse_list_n.go
@@ -0,0 +1,23 @@
+package linkedlist
+
+func ReverseN(head *ListNode, n int) *ListNode {
+ h, _ := helper(head, n)
+ return h
+}
+
+func helper(node *ListNode, n int) (*ListNode, *ListNode) {
+ if n <= 0 || (node == nil || node.Next == nil) { // 边界条件
+ return node, node
+ }
+
+ if n == 1 { // 递归退出条件
+ return node, node.Next
+ }
+
+ last, successor := helper(node.Next, n-1)
+
+ node.Next.Next = node
+ node.Next = successor
+
+ return last, successor
+}
diff --git a/go/linkedlist/reverse_list_n_test.go b/go/linkedlist/reverse_list_n_test.go
new file mode 100644
index 0000000..d489cde
--- /dev/null
+++ b/go/linkedlist/reverse_list_n_test.go
@@ -0,0 +1,55 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_ReverseN(t *testing.T) {
+ ret1 := ReverseN(nil, 0)
+ assert.Nil(t, ret1)
+
+ ret2 := ReverseN(&ListNode{
+ Val: 1,
+ Next: nil,
+ }, 2)
+ assert.Equal(t, ret2, &ListNode{
+ Val: 1,
+ Next: nil,
+ })
+
+ list := &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ Next: nil,
+ },
+ },
+ },
+ },
+ }
+ ret3 := ReverseN(list, 3)
+ assert.Equal(t, ret3, &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 4,
+ Next: &ListNode{
+ Val: 5,
+ Next: nil,
+ },
+ },
+ },
+ },
+ })
+}
diff --git a/go/linkedlist/reverse_list_test.go b/go/linkedlist/reverse_list_test.go
new file mode 100644
index 0000000..4c3e6c8
--- /dev/null
+++ b/go/linkedlist/reverse_list_test.go
@@ -0,0 +1,33 @@
+package linkedlist
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_ReverseList(t *testing.T) {
+ ret1 := ReverseList(nil)
+ assert.Nil(t, ret1)
+
+ ret2 := ReverseList(&ListNode{
+ Val: 1,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 3,
+ Next: nil,
+ },
+ },
+ })
+ assert.Equal(t, ret2, &ListNode{
+ Val: 3,
+ Next: &ListNode{
+ Val: 2,
+ Next: &ListNode{
+ Val: 1,
+ Next: nil,
+ },
+ },
+ })
+}
diff --git a/java/arraylist/MyArrayList.java b/java/arraylist/MyArrayList.java
deleted file mode 100644
index f0247f2..0000000
--- a/java/arraylist/MyArrayList.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package arraylist;
-
-import java.lang.ArrayIndexOutOfBoundsException;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-public class MyArrayList implements Iterable{
- private static final int DEFAULT_CAPACITY = 10;
-
- private int size;
- private AnyType[] items;
-
- public MyArrayList() {
- clear();
- }
-
- public void clear() {
- this.size = 0;
- this.ensureCapacity(DEFAULT_CAPACITY);
- }
-
- public int size() {
- return this.size;
- }
-
- public AnyType get(int index) {
- if (index < 0 || index >= this.size()) {
- throw new ArrayIndexOutOfBoundsException();
- }
- return this.items[index];
- }
-
- public void set(int index, AnyType newVal) {
- if (index < 0 || index >= this.size()) {
- throw new ArrayIndexOutOfBoundsException();
- }
- this.items[index] = newVal;
- }
-
- public void add(int index, AnyType item) {
- if (this.size() == this.items.length) {
- ensureCapacity(2 * this.size());
- }
-
- for (int i = this.size; i > index; i--) {
- this.items[i] = this.items[i - 1];
- }
- this.items[index] = item;
-
- this.size++;
- }
-
- public void add(AnyType item) {
- this.add(this.size(), item);
- }
-
- public AnyType remove(int index) {
- if (index < 0 || index >= this.size()) {
- throw new ArrayIndexOutOfBoundsException();
- }
- AnyType removedItem = this.items[index];
- for (int i = index; i < this.size(); i++) {
- this.items[i] = this.items[i + 1];
- }
-
- this.size--;
- return removedItem;
- }
-
- @SuppressWarnings("unchecked")
- private void ensureCapacity(int newCapacity) {
- if (newCapacity < this.size)
- return;
-
- AnyType[] old = this.items;
- items = (AnyType[]) new Object[newCapacity];
- for (int i = 0; i < this.size(); i++) {
- items[i] = old[i];
- }
- }
-
- @Override
- public Iterator iterator() {
- return new ArrayListIterator();
- }
-
- private class ArrayListIterator implements Iterator {
- private int current = 0;
-
- @Override
- public boolean hasNext() {
- return current < size();
- }
-
- @Override
- public AnyType next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- return items[current++];
- }
-
- @Override
- public void remove() {
- MyArrayList.this.remove(--current);
- }
-
- }
-
-}
diff --git a/java/arraylist/MyArrayListTest.java b/java/arraylist/MyArrayListTest.java
deleted file mode 100644
index 50adc4e..0000000
--- a/java/arraylist/MyArrayListTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package arraylist;
-
-import static org.junit.Assert.*;
-
-import java.util.Iterator;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class MyArrayListTest {
-
- private static MyArrayList list = new MyArrayList();
-
- @Before
- public void setUp() throws Exception {
- list.clear();
- }
-
- @Test
- public void testClear() {
- list.clear();
- assertEquals(0, list.size());
- }
-
- @Test
- public void testSize() {
- assertEquals(0, list.size());
- list.add("1");
- assertEquals(1, list.size());
- }
-
- @Test
- public void testGet() {
- list.add("1");
- list.add("2");
- list.add("3");
- String item = list.get(0);
- assertEquals("1", item);
- }
-
- @Test
- public void testSet() {
- list.add("1");
- list.add("2");
- list.add("3");
- list.set(2, "4");
- assertEquals("4", list.get(2));
- }
-
- @Test
- public void testAddIntAnyType() {
- list.add("1");
- list.add("2");
- list.add("3");
- list.add(0, "4");
- assertEquals("4", list.get(0));
- }
-
- @Test
- public void testAddAnyType() {
- list.add("1");
- list.add("2");
- list.add("3");
- assertEquals(3, list.size());
- assertEquals("3", list.get(2));
- }
-
- @Test
- public void testRemove() {
- list.add("1");
- list.add("2");
- list.add("3");
- list.remove(2);
- assertEquals(2, list.size());
- assertEquals("2", list.get(1));
- }
-
- public void testIterator() {
- list.add("1");
- list.add("2");
- list.add("3");
-
- for (Iterator it = list.iterator(); it.hasNext();) {
- String item = it.next();
- if (item.equals("2")) {
- it.remove();
- }
- }
- assertEquals("3", list.get(1));
- }
-
-}
diff --git a/java/queue/Queue.java b/java/queue/Queue.java
deleted file mode 100644
index 02d1d2d..0000000
--- a/java/queue/Queue.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package queue;
-
-public class Queue {
- private static final int DEFAULT_CAPACITY = 10;
-
- private T[] dataStore;
- private int theSize;
-
- public Queue() {
- this.theSize = 0;
- this.ensureCapacity(DEFAULT_CAPACITY);
- }
-
- public void enqueue(T element) {
- if (this.theSize == this.dataStore.length) {
- this.ensureCapacity(this.theSize *2);
- }
- this.dataStore[this.theSize++] = element;
- }
-
- public T dequeue() {
- if (this.size() == 0) return null;
-
- T d = this.dataStore[0];
- if (this.size() > 1) {
- for (int i = 0; i < this.theSize-1; i++) {
- this.dataStore[i] = this.dataStore[i+1];
- }
- }
- this.theSize--;
-
- return d;
- }
-
- public int size() {
- return this.theSize;
- }
-
- public boolean isEmpty() {
- return this.theSize == 0;
- }
-
- public T font() {
- if (this.size() == 0) return null;
- return this.dataStore[0];
- }
-
- public T end() {
- if (this.size() == 0) return null;
- return this.dataStore[this.theSize-1];
- }
-
- public void clear() {
- this.theSize = 0;
- }
-
- @SuppressWarnings("unchecked")
- private void ensureCapacity(int newCapa) {
- if (this.theSize > newCapa) return;
-
- T[] old = this.dataStore;
- this.dataStore = (T[])new Object[newCapa];
- for (int i = 0; i < this.theSize; i++) {
- this.dataStore[i] = old[i];
- }
- }
-
-}
diff --git a/java/queue/QueueTest.java b/java/queue/QueueTest.java
deleted file mode 100644
index 0f1cf5f..0000000
--- a/java/queue/QueueTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package queue;
-
-import static org.junit.Assert.*;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class QueueTest {
- private static Queue q = new Queue();
-
- @Before
- public void setUp() throws Exception {
- q = new Queue();
- }
-
- @Test
- public void testEnqueue() {
- q.enqueue("1");
- q.enqueue("2");
-
- assertEquals(2, q.size());
- assertEquals(false, q.isEmpty());
- }
-
- @Test
- public void testDequeue() {
- q.enqueue("2");
- q.enqueue("3");
-
- assertEquals("2", q.dequeue());
- assertEquals(1, q.size());
- }
-
- @Test
- public void testSize() {
- q.enqueue("3");
- q.enqueue("4");
- assertEquals(2, q.size());
-
- q.dequeue();
- assertEquals(1, q.size());
-
- q.dequeue();
- assertEquals(0, q.size());
- assertEquals(true, q.isEmpty());
- }
-
- @Test
- public void testIsEmpty() {
- assertEquals(true, q.isEmpty());
- q.enqueue("test");
- assertEquals(false, q.isEmpty());
- }
-
- @Test
- public void testFont() {
- q.enqueue("5");
- q.enqueue("6");
- q.enqueue("7");
-
- assertEquals("5", q.font());
- }
-
- @Test
- public void testEnd() {
- q.enqueue("5");
- q.enqueue("6");
- q.enqueue("7");
-
- assertEquals("7", q.end());
- }
-
- @Test
- public void testClear() {
- q.enqueue("5");
- q.enqueue("7");
-
- q.clear();
- assertEquals(true, q.isEmpty());
- }
-}
diff --git a/java/stack/Stack.java b/java/stack/Stack.java
deleted file mode 100644
index 535eb96..0000000
--- a/java/stack/Stack.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package stack;
-
-public class Stack {
- private static final int DEFAULT_CAPACITY = 10;
-
- private T[] dataStore;
- private int top;
-
- public Stack() {
- this.top = 0;
- this.ensureCapacity(DEFAULT_CAPACITY);
- }
-
- public int size() {
- return this.top;
- }
-
- public boolean isEmpty() {
- return this.top == 0;
- }
-
- public void push(T element) {
- if (this.top == this.dataStore.length) {
- this.ensureCapacity(2*this.size());
- }
- this.dataStore[this.top++] = element;
- }
-
- public T pop() {
- if (this.isEmpty()) return null;
-
- return this.dataStore[--this.top];
- }
-
- public T peek() {
- if (this.isEmpty()) return null;
-
- return this.dataStore[this.top-1];
- }
-
- public void clear() {
- this.top = 0;
- }
-
- @SuppressWarnings("unchecked")
- private void ensureCapacity(int newCapacity) {
- if (newCapacity < this.size()) return;
-
- T[] old = this.dataStore;
- this.dataStore = (T[])new Object[newCapacity];
- for (int i = 0; i < this.size(); i++) {
- this.dataStore[i] = old[i];
- }
- }
-}
diff --git a/java/stack/StackTest.java b/java/stack/StackTest.java
deleted file mode 100644
index 90bf525..0000000
--- a/java/stack/StackTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package stack;
-
-import static org.junit.Assert.*;
-
-import org.junit.Test;
-
-public class StackTest {
-
- @Test
- public void testStack() {
- Stack stack = new Stack();
- assertTrue(stack.isEmpty());
- assertEquals(0, stack.size());
-
- stack.push("1");
- stack.push("2");
- stack.push("3");
- assertEquals(3, stack.size());
-
- String p1 = stack.pop();
- assertEquals("3", p1);
- assertEquals(2, stack.size());
-
- String p2 = stack.peek();
- assertEquals("2", p2);
- assertEquals(2, stack.size());
-
- stack.clear();
- assertTrue(stack.isEmpty());
-
- }
-
-}
diff --git a/javascript/arraylist/ArrayList.js b/javascript/arraylist/ArrayList.js
deleted file mode 100644
index d8d5214..0000000
--- a/javascript/arraylist/ArrayList.js
+++ /dev/null
@@ -1,81 +0,0 @@
-// 迭代器
-function Iterator(list) {
- this.list = list;
- this.current = 0;
-}
-
-Iterator.prototype.hasNext = function() {
- return this.current < this.list.size();
-};
-
-Iterator.prototype.next = function() {
- if (!this.hasNext()) {
- throw new Error('No Such Element Exception!');
- }
-
- return this.list.get(current++);
-};
-
-Iterator.prototype.remove = function() {
- this.list.remove(--current);
-};
-
-
-// ArrayList 构造函数
-function ArrayList() {
- this.dataStore = [];
-}
-
-ArrayList.prototype.size = function() {
- return this.dataStore.length;
-};
-
-ArrayList.prototype.get = function(index) {
- if (index < 0 || index >= this.size()) {
- throw new Error('index:' + index + ' out of bounds exception!');
- }
-
- return this.dataStore[index];
-};
-
-ArrayList.prototype.set = function(index, newVal) {
- if (index < 0 || index >= this.size()) {
- throw new Error('index:' + index + ' out of bounds exception!');
- }
-
- this.dataStore[index] = newVal;
-};
-
-ArrayList.prototype.insert = function(index, val) {
- if (index < 0 || index >= this.size()) {
- throw new Error('index:' + index + ' out of bounds exception!');
- }
-
- this.dataStore.splice(index, 0, val);
-};
-
-ArrayList.prototype.append = function(val) {
- this.dataStore.push(val);
-};
-
-ArrayList.prototype.remove = function(index) {
- if (index < 0 || index >= this.size()) {
- throw new Error('index:' + index + ' out of bounds exception!');
- }
-
- this.dataStore.splice(index, 1);
-};
-
-ArrayList.prototype.clear = function() {
- this.dataStore = [];
-};
-
-ArrayList.prototype.iterator = function() {
- return new Iterator(this);
-};
-
-ArrayList.prototype.toString = function() {
- return this.dataStore.join();
-};
-
-module.exports = ArrayList;
diff --git a/javascript/arraylist/ArrayList_test.js b/javascript/arraylist/ArrayList_test.js
deleted file mode 100644
index e4c7b4a..0000000
--- a/javascript/arraylist/ArrayList_test.js
+++ /dev/null
@@ -1,123 +0,0 @@
-var assert = require('assert');
-var ArrayList = require('./ArrayList.js');
-
-describe('ArrayList', function() {
- describe('#size()', function() {
- it('should return 0 when the ArrayList is empty', function() {
- var arrayList = new ArrayList();
- assert.equal(0, arrayList.size());
- });
-
- it('should return valid value', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
- arrayList.append(3);
- assert.equal(3, arrayList.size());
-
- arrayList.remove(2);
- assert.equal(2, arrayList.size());
- });
- });
-
- describe('#get(index)', function() {
- it('should return valid value', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
- arrayList.append(3);
- assert.equal(3, arrayList.get(2));
- assert.equal(2, arrayList.get(1));
- });
- });
-
- describe('#set(index, newVal)', function() {
- it('should set success and have no error', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
- arrayList.append(3);
- assert.equal(3, arrayList.get(2));
-
- assert.doesNotThrow(function() {
- arrayList.set(2, 4);
- });
- assert.equal(4, arrayList.get(2));
-
- assert.throws(function() {
- arrayList.set(3, 4);
- }, Error);
- });
- });
-
- describe('#insert(index, val)', function() {
- it('should insert success and have no error', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- assert.doesNotThrow(function() {
- arrayList.insert(0, 2);
- });
-
- assert.equal(2, arrayList.get(0));
- assert.equal(2, arrayList.size());
- });
- });
-
- describe('#append(val)', function() {
- it('should append success and have no error', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
-
- assert.equal(1, arrayList.get(0));
- assert.equal(1, arrayList.size());
- });
- });
-
- describe('#remove(index)', function() {
- it('should remove success and have no error', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
- arrayList.append(3);
- assert.equal(3, arrayList.size());
-
- arrayList.remove(0);
- assert.equal(2, arrayList.get(0));
- assert.equal(2, arrayList.size());
- });
- });
-
- describe('#clear()', function() {
- it('should clear success and have no error', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
- arrayList.append(3);
-
- arrayList.clear();
- assert.equal(0, arrayList.size());
- });
- });
-
- describe('#iterator()', function() {
- it('should return a valid value', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
- var iterator = arrayList.iterator();
- assert.notEqual(undefined, iterator);
- assert.notEqual(undefined, iterator.next);
- });
- });
-
- describe('#toString()', function() {
- it('should return a valid value', function() {
- var arrayList = new ArrayList();
- arrayList.append(1);
- arrayList.append(2);
-
- assert.equal('1,2', arrayList.toString());
- });
- });
-
-});
diff --git a/javascript/linkedlist/LinkedList.js b/javascript/linkedlist/LinkedList.js
deleted file mode 100644
index 21e7790..0000000
--- a/javascript/linkedlist/LinkedList.js
+++ /dev/null
@@ -1,84 +0,0 @@
-// Node构造函数
-function Node(element) {
- this.element = element;
- this.prev = null;
- this.next = null;
-}
-
-function LinkedList() {
- this.head = new Node('head');
-}
-
-LinkedList.prototype.find = function(elem) {
- var pos = this.head;
- while (pos.next !== null) {
- pos = pos.next;
- if (pos.element === elem) return pos;
- }
- return null;
-};
-
-LinkedList.prototype.findLast = function() {
- var pos = this.head;
- while (pos.next !== null) {
- pos = pos.next;
- }
- return pos;
-};
-
-LinkedList.prototype.display = function() {
- var pos = this.head;
- while (pos.next !== null) {
- console.log(pos.next.element);
- pos = pos.next;
- }
-};
-
-LinkedList.prototype.dispReverse = function() {
- var pos = this.findLast();
- while (pos.prev !== null) {
- console.log(pos.prev.element);
- pos = pos.prev;
- }
-};
-
-LinkedList.prototype.insert = function(newElem) {
- var node = new Node(newElem);
- var last = this.findLast();
- if (last === null) {
- this.head.next = node;
- node.prev = this.head;
- }
-
- last.next = node;
- node.prev = last;
-};
-
-LinkedList.prototype.insertAfter = function(newElem, afterElem) {
- var node = new Node(newElem);
- var pos = this.find(afterElem);
-
- if (pos === null) return;
- if (pos.next !== null) {
- node.next = pos.next;
- pos.next.prev = node;
- }
-
- node.prev = pos;
- pos.next = node;
-};
-
-LinkedList.prototype.remove = function(elem) {
- var pos = this.find(elem);
- if (pos === null) return;
-
- if (pos.next !== null) {
- pos.next.prev = pos.prev;
- }
- pos.prev.next = pos.next;
- pos.prev = null;
- pos.next = null;
-};
-
-
-module.exports = LinkedList;
diff --git a/javascript/linkedlist/LinkedList_test.js b/javascript/linkedlist/LinkedList_test.js
deleted file mode 100644
index 972aa6a..0000000
--- a/javascript/linkedlist/LinkedList_test.js
+++ /dev/null
@@ -1,25 +0,0 @@
-var assert = require('assert');
-var LinkedList = require('./LinkedList.js');
-
-describe('Queue', function() {
- describe('#insert, #insertAfter, #find, #findLast, #remove', function() {
- it('should all success.', function() {
- var list = new LinkedList();
- list.insert('1');
- list.insert('2');
- list.insert('3');
- assert.equal('2', list.find('3').prev.element);
- assert.equal('3', list.findLast().element);
-
- list.insertAfter('4', '2');
- assert.equal('4', list.find('3').prev.element);
- assert.equal('4', list.find('2').next.element);
- assert.equal('3', list.findLast().element);
-
- list.remove('4');
- assert.equal('2', list.find('3').prev.element);
-
- });
- });
-
-});
diff --git a/javascript/queue/Queue.js b/javascript/queue/Queue.js
deleted file mode 100644
index 1172294..0000000
--- a/javascript/queue/Queue.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Queue构造函数
-function Queue() {
- this.dataStore = [];
-}
-
-
-Queue.prototype.enqueue = function(element) {
- this.dataStore.push(element);
-};
-
-Queue.prototype.dequeue = function() {
- return this.dataStore.shift();
-};
-
-Queue.prototype.size = function() {
- return this.dataStore.length;
-};
-
-Queue.prototype.isEmpty = function() {
- return this.dataStore.length == 0;
-};
-
-Queue.prototype.front = function() {
- if (this.isEmpty()) return null;
- return this.dataStore[0];
-};
-
-Queue.prototype.end = function() {
- if (this.isEmpty()) return null;
- return this.dataStore[this.size()-1];
-};
-
-Queue.prototype.clear = function() {
- this.dataStore.length = 0;
-};
-
-
-module.exports = Queue;
diff --git a/javascript/queue/Queue_test.js b/javascript/queue/Queue_test.js
deleted file mode 100644
index e553fbe..0000000
--- a/javascript/queue/Queue_test.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var assert = require('assert');
-var Queue = require('./Queue.js');
-
-describe('Queue', function() {
- describe('#enqueue, #dequeue, #size, #isEmpty, #front, #end, #clear', function() {
- it('should all success.', function() {
- var q = new Queue();
- q.enqueue(1);
- q.enqueue(2);
- q.enqueue(3);
-
- assert.equal(3, q.size());
- assert.equal(false, q.isEmpty());
-
- var d = q.dequeue();
- assert.equal(1, d);
- assert.equal(2, q.size());
- assert.equal(3, q.end());
- assert.equal(2, q.front());
-
- q.clear();
- assert.equal(true, q.isEmpty());
-
- });
- });
-
-});
diff --git a/javascript/stack/Stack.js b/javascript/stack/Stack.js
deleted file mode 100644
index 709f6b2..0000000
--- a/javascript/stack/Stack.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// Stack构造函数
-function Stack() {
- this.dataStore = [];
-}
-
-Stack.prototype.push = function(element) {
- this.dataStore.push(element);
-};
-
-Stack.prototype.pop = function() {
- return this.dataStore.pop();
-};
-
-Stack.prototype.peek = function() {
- return this.dataStore[this.size()-1];
-};
-
-Stack.prototype.clear = function() {
- this.dataStore.length = 0;
-};
-
-Stack.prototype.size = function() {
- return this.dataStore.length;
-};
-
-Stack.prototype.isEmpty = function() {
- return this.dataStore.length === 0;
-};
-
-module.exports = Stack;
diff --git a/javascript/stack/Stack_test.js b/javascript/stack/Stack_test.js
deleted file mode 100644
index f56863c..0000000
--- a/javascript/stack/Stack_test.js
+++ /dev/null
@@ -1,39 +0,0 @@
-var assert = require('assert');
-var Stack = require('./Stack.js');
-
-describe('Stack', function() {
- describe('#size(), #push()', function() {
- it('should return 1 when push a element into Stack.', function() {
- var stack = new Stack();
- stack.push('1');
- assert.equal(1, stack.size());
- });
- });
-
- describe('#pop(), #peek()', function() {
- it('should return valid value and length.', function() {
- var stack = new Stack();
- stack.push('1');
- stack.push('2');
- stack.push('3');
- stack.push('4');
-
- assert.equal(4, stack.pop());
- assert.equal(3, stack.size());
- assert.equal('3', stack.peek());
- });
- });
-
- describe('#clear(), #isEmpty()', function() {
- it('should return 0, when Stack called clear()', function() {
- var stack = new Stack();
- assert(stack.isEmpty());
- stack.push('1');
- stack.push('2');
- stack.push('3');
- stack.clear();
- assert.equal(0, stack.size());
- });
- });
-
-});
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 0000000..76256b7
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "the_algorithms"
+edition = "2021"
+version = "0.1.0"
+authors = ["Lance Li "]
+
+[dependencies]
+
+[dev-dependencies]
diff --git a/rust/src/arraylist/longest_palindrome.rs b/rust/src/arraylist/longest_palindrome.rs
new file mode 100644
index 0000000..ceaf8c8
--- /dev/null
+++ b/rust/src/arraylist/longest_palindrome.rs
@@ -0,0 +1,44 @@
+pub fn longest_palindrome(s: String) -> String {
+ let mut res = "";
+ for i in 1..s.len() {
+ let s1 = palindrome(&s, i as isize , i as isize);
+ let s2 = palindrome(&s, i as isize, (i+1) as isize);
+
+ if s1.len() > res.len() {
+ res = s1;
+ }
+ if s2.len() > res.len() {
+ res = s2;
+ }
+ }
+
+ res.to_string()
+}
+
+fn palindrome(s: &str, l: isize, r: isize) -> &str {
+ let mut l = l;
+ let mut r = r;
+ let bytes = s.as_bytes();
+ while l >= 0 && r < s.len() as isize && bytes[l as usize] == bytes[r as usize] {
+ l -= 1;
+ r += 1;
+ }
+
+ &s[(l+1) as usize..r as usize]
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_longest_palindrome() {
+ let s = "babad";
+ let res = longest_palindrome(s.to_string());
+ assert_eq!("bab".to_string(), res);
+
+ let s = "cbbd";
+ let res = longest_palindrome(s.to_string());
+ assert_eq!("bb".to_string(), res);
+ }
+}
diff --git a/rust/src/arraylist/mod.rs b/rust/src/arraylist/mod.rs
new file mode 100644
index 0000000..e156010
--- /dev/null
+++ b/rust/src/arraylist/mod.rs
@@ -0,0 +1,9 @@
+pub use move_zeroes::move_zeroes;
+pub use remove_duplicates::remove_duplicates;
+pub use remove_element::remove_element;
+
+mod move_zeroes;
+mod remove_duplicates;
+mod remove_element;
+mod reverse_string;
+mod longest_palindrome;
diff --git a/rust/src/arraylist/move_zeroes.rs b/rust/src/arraylist/move_zeroes.rs
new file mode 100644
index 0000000..c4f7bcc
--- /dev/null
+++ b/rust/src/arraylist/move_zeroes.rs
@@ -0,0 +1,21 @@
+use super::remove_element;
+
+pub fn move_zeroes(nums: &mut Vec) {
+ let mut res = remove_element(nums, 0) as usize;
+ while res < nums.len() {
+ nums[res] = 0;
+ res += 1;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_move_zeroes() {
+ let mut nums = vec![0, 1, 0, 3, 12];
+ move_zeroes(&mut nums);
+ assert_eq!(nums, vec![1, 3, 12, 0, 0]);
+ }
+}
diff --git a/rust/src/arraylist/remove_duplicates.rs b/rust/src/arraylist/remove_duplicates.rs
new file mode 100644
index 0000000..64715fc
--- /dev/null
+++ b/rust/src/arraylist/remove_duplicates.rs
@@ -0,0 +1,33 @@
+pub fn remove_duplicates(nums: &mut Vec) -> i32 {
+ if nums.is_empty() {
+ return 0;
+ }
+
+ let (mut slow, mut fast) = (0, 0);
+ while fast < nums.len() {
+ if nums[slow] != nums[fast] {
+ slow += 1;
+ nums[slow] = nums[fast]
+ }
+ fast += 1;
+ }
+
+ slow as i32 + 1
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_remove_duplicates() {
+ let mut arr = vec![];
+ let res = remove_duplicates(&mut arr);
+ assert_eq!(res, 0);
+
+ arr = vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
+ let res = remove_duplicates(&mut arr);
+ assert_eq!(res, 4);
+ assert_eq!(arr[..4], vec![1, 2, 3, 4]);
+ }
+}
diff --git a/rust/src/arraylist/remove_element.rs b/rust/src/arraylist/remove_element.rs
new file mode 100644
index 0000000..29bb241
--- /dev/null
+++ b/rust/src/arraylist/remove_element.rs
@@ -0,0 +1,28 @@
+pub fn remove_element(nums: &mut Vec, val: i32) -> i32 {
+ let mut slow = 0;
+ let mut fast = 0;
+
+ while fast < nums.len() {
+ if nums[fast] != val {
+ if slow != fast {
+ nums[slow] = nums[fast];
+ }
+ slow += 1;
+ }
+ fast += 1;
+ }
+
+ slow as i32
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_remove_element() {
+ let mut nums = vec![3, 2, 2, 3];
+ assert_eq!(remove_element(&mut nums, 3), 2);
+ assert_eq!(remove_element(&mut nums, 4), 4);
+ }
+}
diff --git a/rust/src/arraylist/reverse_string.rs b/rust/src/arraylist/reverse_string.rs
new file mode 100644
index 0000000..2b1e018
--- /dev/null
+++ b/rust/src/arraylist/reverse_string.rs
@@ -0,0 +1,23 @@
+pub fn reverse_string(s: &mut Vec) {
+ let mut p1 = 0_usize;
+ let mut p2 = s.len()-1;
+ while p1 < p2 {
+ let tmp = s[p1];
+ s[p1] = s[p2];
+ s[p2] = tmp;
+ p1 += 1;
+ p2 -= 1
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_reverse_string() {
+ let mut s = vec!['h', 'e', 'l', 'l', 'o'];
+ reverse_string(&mut s);
+ assert_eq!(vec!['o', 'l', 'l', 'e', 'h'], s)
+ }
+}
\ No newline at end of file
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
new file mode 100644
index 0000000..fde2434
--- /dev/null
+++ b/rust/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod linkedlist;
+pub mod arraylist;
diff --git a/rust/src/linkedlist/detect_cycle.rs b/rust/src/linkedlist/detect_cycle.rs
new file mode 100644
index 0000000..190106c
--- /dev/null
+++ b/rust/src/linkedlist/detect_cycle.rs
@@ -0,0 +1,55 @@
+use std::{cell::RefCell, rc::Rc};
+
+use super::ListNode2;
+
+pub fn detect_cycle(head: Option>>) -> Option>> {
+ let mut slow = head.clone();
+ let mut fast = head.clone();
+
+ while fast.is_some() && fast.as_ref().unwrap().borrow().next.is_some() {
+ slow = slow.unwrap().borrow().next.clone();
+ fast = fast
+ .unwrap()
+ .borrow()
+ .next
+ .as_ref()
+ .unwrap()
+ .borrow()
+ .next
+ .clone();
+
+ if Rc::ptr_eq(slow.as_ref().unwrap(), fast.as_ref().unwrap()) {
+ let mut head = head;
+ while !Rc::ptr_eq(head.as_ref().unwrap(), slow.as_ref().unwrap()) {
+ slow = slow.unwrap().borrow().next.clone();
+ head = head.unwrap().borrow().next.clone();
+ }
+ return head;
+ }
+ }
+
+ None
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_detect_cycle() {
+ let ret = detect_cycle(None);
+ assert!(ret.is_none());
+
+ let head = Some(Rc::new(RefCell::new(ListNode2 { val: 1, next: None })));
+ let node1 = Some(Rc::new(RefCell::new(ListNode2 { val: 2, next: None })));
+ let node2 = Some(Rc::new(RefCell::new(ListNode2 { val: 3, next: None })));
+ let node3 = Some(Rc::new(RefCell::new(ListNode2 { val: 4, next: None })));
+ head.as_ref().unwrap().borrow_mut().next = node1.clone();
+ node1.as_ref().unwrap().borrow_mut().next = node2.clone();
+ node2.as_ref().unwrap().borrow_mut().next = node3.clone();
+ node3.as_ref().unwrap().borrow_mut().next = node2.clone();
+
+ let ret = detect_cycle(head);
+ assert_eq!(ret, node2);
+ }
+}
diff --git a/rust/src/linkedlist/get_intersection_node.rs b/rust/src/linkedlist/get_intersection_node.rs
new file mode 100644
index 0000000..5b4cb43
--- /dev/null
+++ b/rust/src/linkedlist/get_intersection_node.rs
@@ -0,0 +1,81 @@
+use std::{cell::RefCell, rc::Rc};
+
+use super::ListNode2;
+
+pub fn get_intersection_node(
+ head1: Option>>,
+ head2: Option>>,
+) -> Option>> {
+ let mut p1 = head1.clone();
+ let mut p2 = head2.clone();
+ let mut len_a = 0;
+ let mut len_b = 0;
+
+ while p1.is_some() {
+ p1 = p1.unwrap().borrow().next.clone();
+ len_a += 1;
+ }
+ while p2.is_some() {
+ p2 = p2.unwrap().borrow().next.clone();
+ len_b += 1;
+ }
+ if len_a == 0 || len_b == 0 {
+ return None;
+ }
+
+ p1 = head1.clone();
+ p2 = head2.clone();
+ if len_a >= len_b {
+ // A 比 B 长,则A先走 lenA - lenB 步
+ for _ in 0..(len_a - len_b) {
+ p1 = p1.unwrap().borrow().next.clone();
+ }
+ } else {
+ // B 比 A 长,则B先走 lenB - lenA 步
+ for _ in 0..(len_b - len_a) {
+ p2 = p2.unwrap().borrow().next.clone();
+ }
+ }
+
+ // 再一起往后走,直到相遇
+ while !Rc::ptr_eq(p1.as_ref().unwrap(), p2.as_ref().unwrap()) {
+ p1 = p1.unwrap().borrow().next.clone();
+ p2 = p2.unwrap().borrow().next.clone();
+ }
+
+ p1
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_get_intersection_node() {
+ let ret = get_intersection_node(None, None);
+ assert!(ret.is_none());
+
+ let head1 = Some(Rc::new(RefCell::new(ListNode2 { val: 1, next: None })));
+ let head2 = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 10,
+ next: None,
+ })));
+ let node1 = Some(Rc::new(RefCell::new(ListNode2 { val: 2, next: None })));
+ let node2 = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 11,
+ next: None,
+ })));
+ let node_inte = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 3,
+ next: Some(Rc::new(RefCell::new(ListNode2 { val: 4, next: None }))),
+ })));
+
+ head1.as_ref().unwrap().borrow_mut().next = node1.clone();
+ head2.as_ref().unwrap().borrow_mut().next = node2.clone();
+ node1.as_ref().unwrap().borrow_mut().next = node_inte.clone();
+ node2.as_ref().unwrap().borrow_mut().next = node_inte.clone();
+
+ let ret = get_intersection_node(head1, head2);
+ assert_eq!(ret, node_inte.clone());
+ }
+}
diff --git a/rust/src/linkedlist/is_palindrome.rs b/rust/src/linkedlist/is_palindrome.rs
new file mode 100644
index 0000000..73365e7
--- /dev/null
+++ b/rust/src/linkedlist/is_palindrome.rs
@@ -0,0 +1,117 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::ListNode2;
+
+pub fn is_palindrome(head: Option>>) -> bool {
+ if head.is_none() {
+ return false;
+ }
+ if head.as_ref().unwrap().borrow().next.is_none() {
+ return true;
+ }
+ // 1. 找到链表中点
+ let mut slow = head.clone();
+ let mut fast = head.clone();
+ while fast.is_some() && fast.as_ref().unwrap().borrow().next.is_some() {
+ let next = slow.as_ref().unwrap().borrow().next.clone();
+ slow = next;
+ let next2 = fast
+ .as_ref()
+ .unwrap()
+ .borrow()
+ .next
+ .as_ref()
+ .unwrap()
+ .borrow()
+ .next
+ .clone();
+ fast = next2;
+ }
+ let middle = slow;
+
+ // 2. 反转中点之后的链表
+ let mut pre = None;
+ let mut curr = middle.clone();
+ while curr.is_some() {
+ let next = curr.as_ref().unwrap().borrow().next.clone();
+ curr.as_ref().unwrap().borrow_mut().next = pre.take();
+ pre = curr.clone();
+ curr = next;
+ }
+
+ // 3. 比较新节点`new`和老节点`head`是否一致
+ let mut new = pre;
+ let mut old = head.clone();
+ while new.is_some() {
+ if new.as_ref().unwrap().borrow().val != old.as_ref().unwrap().borrow().val {
+ return false;
+ }
+ let new_next = new.as_ref().unwrap().borrow().next.clone();
+ let old_next = old.as_ref().unwrap().borrow().next.clone();
+ new = new_next;
+ old = old_next;
+ }
+
+ true
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_palindrome_v1() {
+ let res = is_palindrome(None);
+ assert!(!res);
+
+ let res = is_palindrome(Some(Rc::new(RefCell::new(ListNode2::new(0)))));
+ assert!(res);
+
+ let head = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 1,
+ next: Some(Rc::new(RefCell::new(ListNode2 { val: 1, next: None }))),
+ })));
+ let res = is_palindrome(head);
+ assert!(res);
+
+ let head = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 1,
+ next: Some(Rc::new(RefCell::new(ListNode2 {
+ val: 2,
+ next: Some(Rc::new(RefCell::new(ListNode2 { val: 1, next: None }))),
+ }))),
+ })));
+ let res = is_palindrome(head);
+ assert!(res);
+
+ let head = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 1,
+ next: Some(Rc::new(RefCell::new(ListNode2 {
+ val: 2,
+ next: Some(Rc::new(RefCell::new(ListNode2 {
+ val: 2,
+ next: Some(Rc::new(RefCell::new(ListNode2 { val: 1, next: None }))),
+ }))),
+ }))),
+ })));
+ let res = is_palindrome(head);
+ assert!(res);
+
+ let head = Some(Rc::new(RefCell::new(ListNode2 {
+ val: 1,
+ next: Some(Rc::new(RefCell::new(ListNode2 {
+ val: 2,
+ next: Some(Rc::new(RefCell::new(ListNode2 {
+ val: 2,
+ next: Some(Rc::new(RefCell::new(ListNode2 {
+ val: 1,
+ next: Some(Rc::new(RefCell::new(ListNode2 { val: 1, next: None }))),
+ }))),
+ }))),
+ }))),
+ })));
+ let res = is_palindrome(head);
+ assert!(!res);
+ }
+}
diff --git a/rust/src/linkedlist/merge_k_lists.rs b/rust/src/linkedlist/merge_k_lists.rs
new file mode 100644
index 0000000..d547bb0
--- /dev/null
+++ b/rust/src/linkedlist/merge_k_lists.rs
@@ -0,0 +1,86 @@
+use super::ListNode;
+use std::cmp::Reverse;
+use std::collections::BinaryHeap;
+
+pub fn merge_k_lists(lists: Vec