Add build script for Go and update Go codes.

This commit is contained in:
krahets 2023-02-09 04:45:06 +08:00
parent 12c085a088
commit e8c78f89f0
39 changed files with 391 additions and 1468 deletions

View File

@ -5,7 +5,6 @@
package chapter_computational_complexity
import (
"fmt"
"math/rand"
)
@ -34,14 +33,3 @@ func findOne(nums []int) int {
}
return -1
}
/* Driver Code */
func main() {
for i := 0; i < 10; i++ {
n := 100
nums := randomNumbers(n)
index := findOne(nums)
fmt.Println("\n数组 [ 1, 2, ..., n ] 被打乱后 =", nums)
fmt.Println("数字 1 的索引为", index)
}
}

View File

@ -14,7 +14,7 @@ func TestWorstBestTimeComplexity(t *testing.T) {
n := 100
nums := randomNumbers(n)
index := findOne(nums)
fmt.Println("打乱后的数组为", nums)
fmt.Println("\n数组 [ 1, 2, ..., n ] 被打乱后 =", nums)
fmt.Println("数字 1 的索引为", index)
}
}

View File

@ -17,6 +17,7 @@ type arrayHashMap struct {
bucket []*entry
}
/* 初始化哈希表 */
func newArrayHashMap() *arrayHashMap {
// 初始化一个长度为 100 的桶(数组)
bucket := make([]*entry, 100)

View File

@ -25,7 +25,7 @@ func linearSearchArray(nums []int, target int) int {
func linearSearchLinkedList(node *ListNode, target int) *ListNode {
// 遍历链表
for node != nil {
// 找到目标元素,返回其索引
// 找到目标结点,返回之
if node.Val == target {
return node
}

View File

@ -4,6 +4,7 @@
package chapter_sorting
/* 插入排序 */
func insertionSort(nums []int) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i := 1; i < len(nums); i++ {

View File

@ -4,7 +4,7 @@
package chapter_sorting
// 合并左子数组和右子数组
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
func merge(nums []int, left, mid, right int) {
@ -37,6 +37,7 @@ func merge(nums []int, left, mid, right int) {
}
}
/* 归并排序 */
func mergeSort(nums []int, left, right int) {
// 终止条件
if left >= right {

View File

@ -12,7 +12,7 @@ type arrayQueue struct {
queCapacity int // 队列容量(即最大容纳元素数量)
}
// newArrayQueue 基于环形数组实现的队列
/* 初始化队列 */
func newArrayQueue(queCapacity int) *arrayQueue {
return &arrayQueue{
nums: make([]int, queCapacity),
@ -22,17 +22,17 @@ func newArrayQueue(queCapacity int) *arrayQueue {
}
}
// size 获取队列的长度
/* 获取队列的长度 */
func (q *arrayQueue) size() int {
return q.queSize
}
// isEmpty 判断队列是否为空
/* 判断队列是否为空 */
func (q *arrayQueue) isEmpty() bool {
return q.queSize == 0
}
// push 入队
/* 入队 */
func (q *arrayQueue) push(num int) {
// 当 rear == queCapacity 表示队列已满
if q.queSize == q.queCapacity {
@ -46,7 +46,7 @@ func (q *arrayQueue) push(num int) {
q.queSize++
}
// poll 出队
/* 出队 */
func (q *arrayQueue) poll() any {
num := q.peek()
// 队首指针向后移动一位,若越过尾部则返回到数组头部
@ -55,7 +55,7 @@ func (q *arrayQueue) poll() any {
return num
}
// peek 访问队首元素
/* 访问队首元素 */
func (q *arrayQueue) peek() any {
if q.isEmpty() {
return nil
@ -63,7 +63,7 @@ func (q *arrayQueue) peek() any {
return q.nums[q.front]
}
// 获取 Slice 用于打印
/* 获取 Slice 用于打印 */
func (q *arrayQueue) toSlice() []int {
rear := (q.front + q.queSize)
if rear >= q.queCapacity {

View File

@ -9,6 +9,7 @@ type arrayStack struct {
data []int // 数据
}
/* 初始化栈 */
func newArrayStack() *arrayStack {
return &arrayStack{
// 设置栈的长度为 0容量为 16
@ -16,34 +17,30 @@ func newArrayStack() *arrayStack {
}
}
// size 栈的长度
/* 栈的长度 */
func (s *arrayStack) size() int {
return len(s.data)
}
// isEmpty 栈是否为空
/* 栈是否为空 */
func (s *arrayStack) isEmpty() bool {
return s.size() == 0
}
// push 入栈
/* 入栈 */
func (s *arrayStack) push(v int) {
// 切片会自动扩容
s.data = append(s.data, v)
}
// pop 出栈
/* 出栈 */
func (s *arrayStack) pop() any {
// 弹出栈前,先判断是否为空
if s.isEmpty() {
return nil
}
val := s.peek()
s.data = s.data[:len(s.data)-1]
return val
}
// peek 获取栈顶元素
/* 获取栈顶元素 */
func (s *arrayStack) peek() any {
if s.isEmpty() {
return nil
@ -52,7 +49,7 @@ func (s *arrayStack) peek() any {
return val
}
// 获取 Slice 用于打印
/* 获取 Slice 用于打印 */
func (s *arrayStack) toSlice() []int {
return s.data
}

View File

@ -8,29 +8,30 @@ import (
"container/list"
)
// linkedListDeque 基于链表实现的双端队列, 使用内置包 list 来实现栈
/* 基于链表实现的双端队列 */
type linkedListDeque struct {
// 使用内置包 list 来实现栈
data *list.List
}
// newLinkedListDeque 初始化双端队列
/* 初始化双端队列 */
func newLinkedListDeque() *linkedListDeque {
return &linkedListDeque{
data: list.New(),
}
}
// pushFirst 队首元素入队
/* 队首元素入队 */
func (s *linkedListDeque) pushFirst(value any) {
s.data.PushFront(value)
}
// pushLast 队尾元素入队
/* 队尾元素入队 */
func (s *linkedListDeque) pushLast(value any) {
s.data.PushBack(value)
}
// pollFirst 队首元素出队
/* 队首元素出队 */
func (s *linkedListDeque) pollFirst() any {
if s.isEmpty() {
return nil
@ -40,7 +41,7 @@ func (s *linkedListDeque) pollFirst() any {
return e.Value
}
// pollLast 队尾元素出队
/* 队尾元素出队 */
func (s *linkedListDeque) pollLast() any {
if s.isEmpty() {
return nil
@ -50,7 +51,7 @@ func (s *linkedListDeque) pollLast() any {
return e.Value
}
// peekFirst 访问队首元素
/* 访问队首元素 */
func (s *linkedListDeque) peekFirst() any {
if s.isEmpty() {
return nil
@ -59,7 +60,7 @@ func (s *linkedListDeque) peekFirst() any {
return e.Value
}
// peekLast 访问队尾元素
/* 访问队尾元素 */
func (s *linkedListDeque) peekLast() any {
if s.isEmpty() {
return nil
@ -68,17 +69,17 @@ func (s *linkedListDeque) peekLast() any {
return e.Value
}
// size 获取队列的长度
/* 获取队列的长度 */
func (s *linkedListDeque) size() int {
return s.data.Len()
}
// isEmpty 判断队列是否为空
/* 判断队列是否为空 */
func (s *linkedListDeque) isEmpty() bool {
return s.data.Len() == 0
}
// 获取 List 用于打印
/* 获取 List 用于打印 */
func (s *linkedListDeque) toList() *list.List {
return s.data
}

View File

@ -14,19 +14,19 @@ type linkedListQueue struct {
data *list.List
}
// newLinkedListQueue 初始化链表
/* 初始化队列 */
func newLinkedListQueue() *linkedListQueue {
return &linkedListQueue{
data: list.New(),
}
}
// push 入队
/* 入队 */
func (s *linkedListQueue) push(value any) {
s.data.PushBack(value)
}
// poll 出队
/* 出队 */
func (s *linkedListQueue) poll() any {
if s.isEmpty() {
return nil
@ -36,7 +36,7 @@ func (s *linkedListQueue) poll() any {
return e.Value
}
// peek 访问队首元素
/* 访问队首元素 */
func (s *linkedListQueue) peek() any {
if s.isEmpty() {
return nil
@ -45,17 +45,17 @@ func (s *linkedListQueue) peek() any {
return e.Value
}
// size 获取队列的长度
/* 获取队列的长度 */
func (s *linkedListQueue) size() int {
return s.data.Len()
}
// isEmpty 判断队列是否为空
/* 判断队列是否为空 */
func (s *linkedListQueue) isEmpty() bool {
return s.data.Len() == 0
}
// 获取 List 用于打印
/* 获取 List 用于打印 */
func (s *linkedListQueue) toList() *list.List {
return s.data
}

View File

@ -14,19 +14,19 @@ type linkedListStack struct {
data *list.List
}
// newLinkedListStack 初始化链表
/* 初始化栈 */
func newLinkedListStack() *linkedListStack {
return &linkedListStack{
data: list.New(),
}
}
// push 入栈
/* 入栈 */
func (s *linkedListStack) push(value int) {
s.data.PushBack(value)
}
// pop 出栈
/* 出栈 */
func (s *linkedListStack) pop() any {
if s.isEmpty() {
return nil
@ -36,7 +36,7 @@ func (s *linkedListStack) pop() any {
return e.Value
}
// peek 访问栈顶元素
/* 访问栈顶元素 */
func (s *linkedListStack) peek() any {
if s.isEmpty() {
return nil
@ -45,17 +45,17 @@ func (s *linkedListStack) peek() any {
return e.Value
}
// size 获取栈的长度
/* 获取栈的长度 */
func (s *linkedListStack) size() int {
return s.data.Len()
}
// isEmpty 判断栈是否为空
/* 判断栈是否为空 */
func (s *linkedListStack) isEmpty() bool {
return s.data.Len() == 0
}
// 获取 List 用于打印
/* 获取 List 用于打印 */
func (s *linkedListStack) toList() *list.List {
return s.data
}

View File

@ -7,17 +7,17 @@ package chapter_tree
import . "github.com/krahets/hello-algo/pkg"
/* AVL 树 */
type avlTree struct {
type aVLTree struct {
// 根结点
root *TreeNode
}
func newAVLTree() *avlTree {
return &avlTree{root: nil}
func newAVLTree() *aVLTree {
return &aVLTree{root: nil}
}
/* 获取结点高度 */
func height(node *TreeNode) int {
func (t *aVLTree) height(node *TreeNode) int {
// 空结点高度为 -1 ,叶结点高度为 0
if node != nil {
return node.Height
@ -26,9 +26,9 @@ func height(node *TreeNode) int {
}
/* 更新结点高度 */
func updateHeight(node *TreeNode) {
lh := height(node.Left)
rh := height(node.Right)
func (t *aVLTree) updateHeight(node *TreeNode) {
lh := t.height(node.Left)
rh := t.height(node.Right)
// 结点高度等于最高子树高度 + 1
if lh > rh {
node.Height = lh + 1
@ -38,68 +38,68 @@ func updateHeight(node *TreeNode) {
}
/* 获取平衡因子 */
func balanceFactor(node *TreeNode) int {
func (t *aVLTree) balanceFactor(node *TreeNode) int {
// 空结点平衡因子为 0
if node == nil {
return 0
}
// 结点平衡因子 = 左子树高度 - 右子树高度
return height(node.Left) - height(node.Right)
return t.height(node.Left) - t.height(node.Right)
}
/* 右旋操作 */
func rightRotate(node *TreeNode) *TreeNode {
func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode {
child := node.Left
grandChild := child.Right
// 以 child 为原点,将 node 向右旋转
child.Right = node
node.Left = grandChild
// 更新结点高度
updateHeight(node)
updateHeight(child)
t.updateHeight(node)
t.updateHeight(child)
// 返回旋转后子树的根结点
return child
}
/* 左旋操作 */
func leftRotate(node *TreeNode) *TreeNode {
func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode {
child := node.Right
grandChild := child.Left
// 以 child 为原点,将 node 向左旋转
child.Left = node
node.Right = grandChild
// 更新结点高度
updateHeight(node)
updateHeight(child)
t.updateHeight(node)
t.updateHeight(child)
// 返回旋转后子树的根结点
return child
}
/* 执行旋转操作,使该子树重新恢复平衡 */
func rotate(node *TreeNode) *TreeNode {
func (t *aVLTree) rotate(node *TreeNode) *TreeNode {
// 获取结点 node 的平衡因子
// Go 推荐短变量,这里 bf 指代 balanceFactor
bf := balanceFactor(node)
// Go 推荐短变量,这里 bf 指代 t.balanceFactor
bf := t.balanceFactor(node)
// 左偏树
if bf > 1 {
if balanceFactor(node.Left) >= 0 {
if t.balanceFactor(node.Left) >= 0 {
// 右旋
return rightRotate(node)
return t.rightRotate(node)
} else {
// 先左旋后右旋
node.Left = leftRotate(node.Left)
return rightRotate(node)
node.Left = t.leftRotate(node.Left)
return t.rightRotate(node)
}
}
// 右偏树
if bf < -1 {
if balanceFactor(node.Right) <= 0 {
if t.balanceFactor(node.Right) <= 0 {
// 左旋
return leftRotate(node)
return t.leftRotate(node)
} else {
// 先右旋后左旋
node.Right = rightRotate(node.Right)
return leftRotate(node)
node.Right = t.rightRotate(node.Right)
return t.leftRotate(node)
}
}
// 平衡树,无需旋转,直接返回
@ -107,49 +107,49 @@ func rotate(node *TreeNode) *TreeNode {
}
/* 插入结点 */
func (t *avlTree) insert(val int) *TreeNode {
t.root = insertHelper(t.root, val)
func (t *aVLTree) insert(val int) *TreeNode {
t.root = t.insertHelper(t.root, val)
return t.root
}
/* 递归插入结点(辅助函数) */
func insertHelper(node *TreeNode, val int) *TreeNode {
func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode {
if node == nil {
return NewTreeNode(val)
}
/* 1. 查找插入位置,并插入结点 */
if val < node.Val {
node.Left = insertHelper(node.Left, val)
node.Left = t.insertHelper(node.Left, val)
} else if val > node.Val {
node.Right = insertHelper(node.Right, val)
node.Right = t.insertHelper(node.Right, val)
} else {
// 重复结点不插入,直接返回
return node
}
// 更新结点高度
updateHeight(node)
t.updateHeight(node)
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node)
node = t.rotate(node)
// 返回子树的根结点
return node
}
/* 删除结点 */
func (t *avlTree) remove(val int) *TreeNode {
root := removeHelper(t.root, val)
func (t *aVLTree) remove(val int) *TreeNode {
root := t.removeHelper(t.root, val)
return root
}
/* 递归删除结点(辅助函数) */
func removeHelper(node *TreeNode, val int) *TreeNode {
func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode {
if node == nil {
return nil
}
/* 1. 查找结点,并删除之 */
if val < node.Val {
node.Left = removeHelper(node.Left, val)
node.Left = t.removeHelper(node.Left, val)
} else if val > node.Val {
node.Right = removeHelper(node.Right, val)
node.Right = t.removeHelper(node.Right, val)
} else {
if node.Left == nil || node.Right == nil {
child := node.Left
@ -165,21 +165,21 @@ func removeHelper(node *TreeNode, val int) *TreeNode {
}
} else {
// 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点
temp := getInOrderNext(node.Right)
node.Right = removeHelper(node.Right, temp.Val)
temp := t.getInOrderNext(node.Right)
node.Right = t.removeHelper(node.Right, temp.Val)
node.Val = temp.Val
}
}
// 更新结点高度
updateHeight(node)
t.updateHeight(node)
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node)
node = t.rotate(node)
// 返回子树的根结点
return node
}
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
func getInOrderNext(node *TreeNode) *TreeNode {
func (t *aVLTree) getInOrderNext(node *TreeNode) *TreeNode {
if node == nil {
return node
}
@ -191,7 +191,7 @@ func getInOrderNext(node *TreeNode) *TreeNode {
}
/* 查找结点 */
func (t *avlTree) search(val int) *TreeNode {
func (t *aVLTree) search(val int) *TreeNode {
cur := t.root
// 循环查找,越过叶结点后跳出
for cur != nil {

View File

@ -41,13 +41,13 @@ func TestAVLTree(t *testing.T) {
fmt.Printf("\n查找到的结点对象为 %#v ,结点值 = %d \n", node, node.Val)
}
func testInsert(tree *avlTree, val int) {
func testInsert(tree *aVLTree, val int) {
tree.insert(val)
fmt.Printf("\n插入结点 %d 后AVL 树为 \n", val)
PrintTree(tree.root)
}
func testRemove(tree *avlTree, val int) {
func testRemove(tree *aVLTree, val int) {
tree.remove(val)
fmt.Printf("\n删除结点 %d 后AVL 树为 \n", val)
PrintTree(tree.root)

View File

@ -15,12 +15,26 @@ type binarySearchTree struct {
}
func newBinarySearchTree(nums []int) *binarySearchTree {
// sorting array
// 排序数组
sort.Ints(nums)
root := buildBinarySearchTree(nums, 0, len(nums)-1)
return &binarySearchTree{
root: root,
// 构建二叉搜索树
bst := &binarySearchTree{}
bst.root = bst.buildTree(nums, 0, len(nums)-1)
return bst
}
/* 构建二叉搜索树 */
func (bst *binarySearchTree) buildTree(nums []int, left, right int) *TreeNode {
if left > right {
return nil
}
// 将数组中间结点作为根结点
middle := left + (right-left)>>1
root := NewTreeNode(nums[middle])
// 递归构建左子树和右子树
root.Left = bst.buildTree(nums, left, middle-1)
root.Right = bst.buildTree(nums, middle+1, right)
return root
}
/* 获取根结点 */
@ -146,21 +160,7 @@ func (bst *binarySearchTree) remove(num int) *TreeNode {
return cur
}
// buildBinarySearchTree Build a binary search tree from array.
func buildBinarySearchTree(nums []int, left, right int) *TreeNode {
if left > right {
return nil
}
// 将数组中间结点作为根结点
middle := left + (right-left)>>1
root := NewTreeNode(nums[middle])
// 递归构建左子树和右子树
root.Left = buildBinarySearchTree(nums, left, middle-1)
root.Right = buildBinarySearchTree(nums, middle+1, right)
return root
}
// print binary search tree
/* 打印二叉搜索树 */
func (bst *binarySearchTree) print() {
PrintTree(bst.root)
}

View File

@ -11,7 +11,7 @@ import (
)
/* 层序遍历 */
func levelOrder(root *TreeNode) []int {
func hierOrder(root *TreeNode) []int {
// 初始化队列,加入根结点
queue := list.New()
queue.PushBack(root)

View File

@ -11,7 +11,7 @@ import (
. "github.com/krahets/hello-algo/pkg"
)
func TestLevelOrder(t *testing.T) {
func TestHierOrder(t *testing.T) {
/* 初始化二叉树 */
// 这里借助了一个从数组直接生成二叉树的函数
root := ArrToTree([]any{1, 2, 3, 4, 5, 6, 7})
@ -19,6 +19,6 @@ func TestLevelOrder(t *testing.T) {
PrintTree(root)
// 层序遍历
nums := levelOrder(root)
nums := hierOrder(root)
fmt.Println("\n层序遍历的结点打印序列 =", nums)
}

View File

@ -135,14 +135,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Go"
```go title="array.go"
/* 随机返回一个数组元素 */
func randomAccess(nums []int) (randomNum int) {
// 在区间 [0, nums.length) 中随机抽取一个数字
randomIndex := rand.Intn(len(nums))
// 获取并返回随机元素
randomNum = nums[randomIndex]
return
}
[class]{}-[func]{randomAccess}
```
=== "JavaScript"
@ -213,17 +206,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Go"
```go title="array.go"
/* 扩展数组长度 */
func extend(nums []int, enlarge int) []int {
// 初始化一个扩展长度后的数组
res := make([]int, len(nums)+enlarge)
// 将原数组中的所有元素复制到新数组
for i, num := range nums {
res[i] = num
}
// 返回扩展后的新数组
return res
}
[class]{}-[func]{extend}
```
=== "JavaScript"
@ -308,23 +291,9 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Go"
```go title="array.go"
/* 在数组的索引 index 处插入元素 num */
func insert(nums []int, num int, index int) {
// 把索引 index 以及之后的所有元素向后移动一位
for i := len(nums) - 1; i > index; i-- {
nums[i] = nums[i-1]
}
// 将 num 赋给 index 处元素
nums[index] = num
}
[class]{}-[func]{insert}
/* 删除索引 index 处元素 */
func remove(nums []int, index int) {
// 把索引 index 之后的所有元素向前移动一位
for i := index; i < len(nums)-1; i++ {
nums[i] = nums[i+1]
}
}
[class]{}-[func]{remove}
```
=== "JavaScript"
@ -414,18 +383,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Go"
```go title="array.go"
/* 遍历数组 */
func traverse(nums []int) {
count := 0
// 通过索引遍历数组
for i := 0; i < len(nums); i++ {
count++
}
// 直接遍历数组
for range nums {
count++
}
}
[class]{}-[func]{traverse}
```
=== "JavaScript"
@ -500,17 +458,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Go"
```go title="array.go"
/* 在数组中查找指定元素 */
func find(nums []int, target int) (index int) {
index = -1
for i := 0; i < len(nums); i++ {
if nums[i] == target {
index = i
break
}
}
return
}
[class]{}-[func]{find}
```
=== "JavaScript"

View File

@ -333,24 +333,9 @@ comments: true
=== "C++"
```cpp title="linked_list.cpp"
/* 在链表的结点 n0 之后插入结点 P */
void insert(ListNode* n0, ListNode* P) {
ListNode* n1 = n0->next;
n0->next = P;
P->next = n1;
}
[class]{}-[func]{insert}
/* 删除链表的结点 n0 之后的首个结点 */
void remove(ListNode* n0) {
if (n0->next == nullptr)
return;
// n0 -> P -> n1
ListNode* P = n0->next;
ListNode* n1 = P->next;
n0->next = n1;
// 释放内存
delete P;
}
[class]{}-[func]{remove}
```
=== "Python"
@ -364,66 +349,25 @@ comments: true
=== "Go"
```go title="linked_list.go"
/* 在链表的结点 n0 之后插入结点 P */
func insert(n0 *ListNode, P *ListNode) {
n1 := n0.Next
n0.Next = P
P.Next = n1
}
[class]{}-[func]{insertNode}
/* 删除链表的结点 n0 之后的首个结点 */
func removeNode(n0 *ListNode) {
if n0.Next == nil {
return
}
// n0 -> P -> n1
P := n0.Next
n1 := P.Next
n0.Next = n1
}
[class]{}-[func]{removeNode}
```
=== "JavaScript"
```javascript title="linked_list.js"
/* 在链表的结点 n0 之后插入结点 P */
function insert(n0, P) {
let n1 = n0.next;
n0.next = P;
P.next = n1;
}
[class]{}-[func]{insert}
/* 删除链表的结点 n0 之后的首个结点 */
function remove(n0) {
if (!n0.next)
return;
// n0 -> P -> n1
let P = n0.next;
let n1 = P.next;
n0.next = n1;
}
[class]{}-[func]{remove}
```
=== "TypeScript"
```typescript title="linked_list.ts"
/* 在链表的结点 n0 之后插入结点 P */
function insert(n0: ListNode, P: ListNode): void {
const n1 = n0.next;
n0.next = P;
P.next = n1;
}
[class]{}-[func]{insert}
/* 删除链表的结点 n0 之后的首个结点 */
function remove(n0: ListNode): void {
if (!n0.next) {
return;
}
// n0 -> P -> n1
const P = n0.next;
const n1 = P.next;
n0.next = n1;
}
[class]{}-[func]{remove}
```
=== "C"
@ -443,24 +387,9 @@ comments: true
=== "Swift"
```swift title="linked_list.swift"
/* 在链表的结点 n0 之后插入结点 P */
func insert(n0: ListNode, P: ListNode) {
let n1 = n0.next
n0.next = P
P.next = n1
}
[class]{}-[func]{insert}
/* 删除链表的结点 n0 之后的首个结点 */
func remove(n0: ListNode) {
if n0.next == nil {
return
}
// n0 -> P -> n1
let P = n0.next
let n1 = P?.next
n0.next = n1
P?.next = nil
}
[class]{}-[func]{remove}
```
=== "Zig"
@ -508,16 +437,7 @@ comments: true
=== "Go"
```go title="linked_list.go"
/* 访问链表中索引为 index 的结点 */
func access(head *ListNode, index int) *ListNode {
for i := 0; i < index; i++ {
if head == nil {
return nil
}
head = head.Next
}
return head
}
[class]{}-[func]{access}
```
=== "JavaScript"
@ -592,18 +512,7 @@ comments: true
=== "Go"
```go title="linked_list.go"
/* 在链表中查找值为 target 的首个结点 */
func find(head *ListNode, target int) int {
index := 0
for head != nil {
if head.Val == target {
return index
}
head = head.Next
index++
}
return -1
}
[class]{}-[func]{findNode}
```
=== "JavaScript"

View File

@ -734,103 +734,7 @@ comments: true
=== "Go"
```go title="my_list.go"
/* 列表类简易实现 */
type myList struct {
numsCapacity int
nums []int
numsSize int
extendRatio int
}
/* 构造函数 */
func newMyList() *myList {
return &myList{
numsCapacity: 10, // 列表容量
nums: make([]int, 10), // 数组(存储列表元素)
numsSize: 0, // 列表长度(即当前元素数量)
extendRatio: 2, // 每次列表扩容的倍数
}
}
/* 获取列表长度(即当前元素数量) */
func (l *myList) size() int {
return l.numsSize
}
/* 获取列表容量 */
func (l *myList) capacity() int {
return l.numsCapacity
}
/* 访问元素 */
func (l *myList) get(index int) int {
// 索引如果越界则抛出异常,下同
if index < 0 || index >= l.numsSize {
panic("索引越界")
}
return l.nums[index]
}
/* 更新元素 */
func (l *myList) set(num, index int) {
if index < 0 || index >= l.numsSize {
panic("索引越界")
}
l.nums[index] = num
}
/* 尾部添加元素 */
func (l *myList) add(num int) {
// 元素数量超出容量时,触发扩容机制
if l.numsSize == l.numsCapacity {
l.extendCapacity()
}
l.nums[l.numsSize] = num
// 更新元素数量
l.numsSize++
}
/* 中间插入元素 */
func (l *myList) insert(num, index int) {
if index < 0 || index >= l.numsSize {
panic("索引越界")
}
// 元素数量超出容量时,触发扩容机制
if l.numsSize == l.numsCapacity {
l.extendCapacity()
}
// 索引 i 以及之后的元素都向后移动一位
for j := l.numsSize - 1; j >= index; j-- {
l.nums[j+1] = l.nums[j]
}
l.nums[index] = num
// 更新元素数量
l.numsSize++
}
/* 删除元素 */
func (l *myList) remove(index int) int {
if index < 0 || index >= l.numsSize {
panic("索引越界")
}
num := l.nums[index]
// 索引 i 之后的元素都向前移动一位
for j := index; j < l.numsSize-1; j++ {
l.nums[j] = l.nums[j+1]
}
// 更新元素数量
l.numsSize--
// 返回被删除元素
return num
}
/* 列表扩容 */
func (l *myList) extendCapacity() {
// 新建一个长度为 self.__size 的数组,并将原数组拷贝到新数组
l.nums = append(l.nums, make([]int, l.numsCapacity*(l.extendRatio-1))...)
// 更新列表容量
l.numsCapacity = len(l.nums)
}
[class]{myList}-[func]{}
```
=== "JavaScript"

View File

@ -600,24 +600,7 @@ $$
=== "Go"
```go title="space_complexity.go"
/* 常数阶 */
func spaceConstant(n int) {
// 常量、变量、对象占用 O(1) 空间
const a = 0
b := 0
nums := make([]int, 10000)
ListNode := newNode(0)
// 循环中的变量占用 O(1) 空间
var c int
for i := 0; i < n; i++ {
c = 0
}
// 循环中的函数占用 O(1) 空间
for i := 0; i < n; i++ {
function()
}
fmt.Println(a, b, nums, c, ListNode)
}
[class]{}-[func]{spaceConstant}
```
=== "JavaScript"
@ -703,21 +686,7 @@ $$
=== "Go"
```go title="space_complexity.go"
/* 线性阶 */
func spaceLinear(n int) {
// 长度为 n 的数组占用 O(n) 空间
_ = make([]int, n)
// 长度为 n 的列表占用 O(n) 空间
var nodes []*node
for i := 0; i < n; i++ {
nodes = append(nodes, newNode(i))
}
// 长度为 n 的哈希表占用 O(n) 空间
m := make(map[int]string, n)
for i := 0; i < n; i++ {
m[i] = strconv.Itoa(i)
}
}
[class]{}-[func]{spaceLinear}
```
=== "JavaScript"
@ -800,14 +769,7 @@ $$
=== "Go"
```go title="space_complexity.go"
/* 线性阶(递归实现) */
func spaceLinearRecur(n int) {
fmt.Println("递归 n =", n)
if n == 1 {
return
}
spaceLinearRecur(n - 1)
}
[class]{}-[func]{spaceLinearRecur}
```
=== "JavaScript"
@ -880,14 +842,7 @@ $$
=== "Go"
```go title="space_complexity.go"
/* 平方阶 */
func spaceQuadratic(n int) {
// 矩阵占用 O(n^2) 空间
numMatrix := make([][]int, n)
for i := 0; i < n; i++ {
numMatrix[i] = make([]int, n)
}
}
[class]{}-[func]{spaceQuadratic}
```
=== "JavaScript"
@ -964,15 +919,7 @@ $$
=== "Go"
```go title="space_complexity.go"
/* 平方阶(递归实现) */
func spaceQuadraticRecur(n int) int {
if n <= 0 {
return 0
}
// 数组 nums 长度为 n, n-1, ..., 2, 1
nums := make([]int, n)
return spaceQuadraticRecur(n - 1)
}
[class]{}-[func]{spaceQuadraticRecur}
```
=== "JavaScript"
@ -1046,16 +993,7 @@ $$
=== "Go"
```go title="space_complexity.go"
/* 指数阶(建立满二叉树) */
func buildTree(n int) *treeNode {
if n == 0 {
return nil
}
root := newTreeNode(0)
root.left = buildTree(n - 1)
root.right = buildTree(n - 1)
return root
}
[class]{}-[func]{buildTree}
```
=== "JavaScript"

View File

@ -51,18 +51,7 @@ comments: true
=== "Go"
```go title="leetcode_two_sum.go"
func twoSumBruteForce(nums []int, target int) []int {
size := len(nums)
// 两层循环,时间复杂度 O(n^2)
for i := 0; i < size-1; i++ {
for j := i + 1; i < size; j++ {
if nums[i]+nums[j] == target {
return []int{i, j}
}
}
}
return nil
}
[class]{}-[func]{twoSumBruteForce}
```
=== "JavaScript"
@ -144,18 +133,7 @@ comments: true
=== "Go"
```go title="leetcode_two_sum.go"
func twoSumHashTable(nums []int, target int) []int {
// 辅助哈希表,空间复杂度 O(n)
hashTable := map[int]int{}
// 单层循环,时间复杂度 O(n)
for idx, val := range nums {
if preIdx, ok := hashTable[target-val]; ok {
return []int{preIdx, idx}
}
hashTable[val] = idx
}
return nil
}
[class]{}-[func]{twoSumHashTable}
```
=== "JavaScript"

View File

@ -813,15 +813,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 常数阶 */
func constant(n int) int {
count := 0
size := 100000
for i := 0; i < size; i++ {
count ++
}
return count
}
[class]{}-[func]{constant}
```
=== "JavaScript"
@ -904,14 +896,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 线性阶 */
func linear(n int) int {
count := 0
for i := 0; i < n; i++ {
count++
}
return count
}
[class]{}-[func]{linear}
```
=== "JavaScript"
@ -992,15 +977,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 线性阶(遍历数组) */
func arrayTraversal(nums []int) int {
count := 0
// 循环次数与数组长度成正比
for range nums {
count++
}
return count
}
[class]{}-[func]{arrayTraversal}
```
=== "JavaScript"
@ -1080,17 +1057,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 平方阶 */
func quadratic(n int) int {
count := 0
// 循环次数与数组长度成平方关系
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
count++
}
}
return count
}
[class]{}-[func]{quadratic}
```
=== "JavaScript"
@ -1182,24 +1149,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 平方阶(冒泡排序) */
func bubbleSort(nums []int) int {
count := 0 // 计数器
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i := len(nums) - 1; i > 0; i-- {
// 内循环:冒泡操作
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// 交换 nums[j] 与 nums[j + 1]
tmp := nums[j]
nums[j] = nums[j+1]
nums[j+1] = tmp
count += 3 // 元素交换包含 3 个单元操作
}
}
}
return count
}
[class]{}-[func]{bubbleSort}
```
=== "JavaScript"
@ -1305,19 +1255,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 指数阶(循环实现)*/
func exponential(n int) int {
count, base := 0, 1
// cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1)
for i := 0; i < n; i++ {
for j := 0; j < base; j++ {
count++
}
base *= 2
}
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
return count
}
[class]{}-[func]{exponential}
```
=== "JavaScript"
@ -1411,13 +1349,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 指数阶(递归实现)*/
func expRecur(n int) int {
if n == 1 {
return 1
}
return expRecur(n-1) + expRecur(n-1) + 1
}
[class]{}-[func]{expRecur}
```
=== "JavaScript"
@ -1493,15 +1425,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 对数阶(循环实现)*/
func logarithmic(n float64) int {
count := 0
for n > 1 {
n = n / 2
count++
}
return count
}
[class]{}-[func]{logarithmic}
```
=== "JavaScript"
@ -1586,13 +1510,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 对数阶(递归实现)*/
func logRecur(n float64) int {
if n <= 1 {
return 0
}
return logRecur(n/2) + 1
}
[class]{}-[func]{logRecur}
```
=== "JavaScript"
@ -1667,18 +1585,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 线性对数阶 */
func linearLogRecur(n float64) int {
if n <= 1 {
return 1
}
count := linearLogRecur(n/2) +
linearLogRecur(n/2)
for i := 0.0; i < n; i++ {
count++
}
return count
}
[class]{}-[func]{linearLogRecur}
```
=== "JavaScript"
@ -1772,18 +1679,7 @@ $$
=== "Go"
```go title="time_complexity.go"
/* 阶乘阶(递归实现) */
func factorialRecur(n int) int {
if n == 0 {
return 1
}
count := 0
// 从 1 个分裂出 n 个
for i := 0; i < n; i++ {
count += factorialRecur(n - 1)
}
return count
}
[class]{}-[func]{factorialRecur}
```
=== "JavaScript"

View File

@ -440,52 +440,9 @@ $$
=== "Go"
```go title="array_hash_map.go"
/* 键值对 int->String */
type entry struct {
key int
val string
}
[class]{entry}-[func]{}
/* 基于数组简易实现的哈希表 */
type arrayHashMap struct {
bucket []*entry
}
func newArrayHashMap() *arrayHashMap {
// 初始化一个长度为 100 的桶(数组)
bucket := make([]*entry, 100)
return &arrayHashMap{bucket: bucket}
}
/* 哈希函数 */
func (a *arrayHashMap) hashFunc(key int) int {
index := key % 100
return index
}
/* 查询操作 */
func (a *arrayHashMap) get(key int) string {
index := a.hashFunc(key)
pair := a.bucket[index]
if pair == nil {
return "Not Found"
}
return pair.val
}
/* 添加操作 */
func (a *arrayHashMap) put(key int, val string) {
pair := &entry{key: key, val: val}
index := a.hashFunc(key)
a.bucket[index] = pair
}
/* 删除操作 */
func (a *arrayHashMap) remove(key int) {
index := a.hashFunc(key)
// 置为 nil ,代表删除
a.bucket[index] = nil
}
[class]{arrayHashMap}-[func]{}
```
=== "JavaScript"

View File

@ -286,33 +286,11 @@ comments: true
=== "Go"
```go title="my_heap.go"
type maxHeap struct {
// 使用切片而非数组,这样无需考虑扩容问题
data []any
}
[class]{maxHeap}-[func]{left}
/* 构造函数,建立空堆 */
func newHeap() *maxHeap {
return &maxHeap{
data: make([]any, 0),
}
}
[class]{maxHeap}-[func]{right}
/* 获取左子结点索引 */
func (h *maxHeap) left(i int) int {
return 2*i + 1
}
/* 获取右子结点索引 */
func (h *maxHeap) right(i int) int {
return 2*i + 2
}
/* 获取父结点索引 */
func (h *maxHeap) parent(i int) int {
// 向下整除
return (i - 1) / 2
}
[class]{maxHeap}-[func]{parent}
```
=== "JavaScript"
@ -392,10 +370,7 @@ comments: true
=== "Go"
```go title="my_heap.go"
/* 访问堆顶元素 */
func (h *maxHeap) peek() any {
return h.data[0]
}
[class]{maxHeap}-[func]{peek}
```
=== "JavaScript"
@ -485,29 +460,9 @@ comments: true
=== "Go"
```go title="my_heap.go"
/* 元素入堆 */
func (h *maxHeap) push(val any) {
// 添加结点
h.data = append(h.data, val)
// 从底至顶堆化
h.siftUp(len(h.data) - 1)
}
[class]{maxHeap}-[func]{push}
/* 从结点 i 开始,从底至顶堆化 */
func (h *maxHeap) siftUp(i int) {
for true {
// 获取结点 i 的父结点
p := h.parent(i)
// 当“越过根结点”或“结点无需修复”时,结束堆化
if p < 0 || h.data[i].(int) <= h.data[p].(int) {
break
}
// 交换两结点
h.swap(i, p)
// 循环向上堆化
i = p
}
}
[class]{maxHeap}-[func]{siftUp}
```
=== "JavaScript"
@ -621,46 +576,9 @@ comments: true
=== "Go"
```go title="my_heap.go"
/* 元素出堆 */
func (h *maxHeap) poll() any {
// 判空处理
if h.isEmpty() {
fmt.Println("error")
return nil
}
// 交换根结点与最右叶结点(即交换首元素与尾元素)
h.swap(0, h.size()-1)
// 删除结点
val := h.data[len(h.data)-1]
h.data = h.data[:len(h.data)-1]
// 从顶至底堆化
h.siftDown(0)
[class]{maxHeap}-[func]{poll}
// 返回堆顶元素
return val
}
/* 从结点 i 开始,从顶至底堆化 */
func (h *maxHeap) siftDown(i int) {
for true {
// 判断结点 i, l, r 中值最大的结点,记为 max
l, r, max := h.left(i), h.right(i), i
if l < h.size() && h.data[l].(int) > h.data[max].(int) {
max = l
}
if r < h.size() && h.data[r].(int) > h.data[max].(int) {
max = r
}
// 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出
if max == i {
break
}
// 交换两结点
h.swap(i, max)
// 循环向下堆化
i = max
}
}
[class]{maxHeap}-[func]{siftDown}
```
=== "JavaScript"
@ -734,16 +652,7 @@ comments: true
=== "Go"
```go title="my_heap.go"
/* 构造函数,根据切片建堆 */
func newMaxHeap(nums []any) *maxHeap {
// 将列表元素原封不动添加进堆
h := &maxHeap{data: nums}
// 堆化除叶结点以外的其他所有结点
for i := len(h.data) - 1; i >= 0; i-- {
h.siftDown(i)
}
return h
}
[class]{maxHeap}-[func]{newMaxHeap}
```
=== "JavaScript"

View File

@ -72,24 +72,7 @@ $$
=== "Go"
```go title="binary_search.go"
/* 二分查找(双闭区间) */
func binarySearch(nums []int, target int) int {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
i, j := 0, len(nums)-1
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
for i <= j {
m := (i + j) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j]
i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1
} else { // 找到目标元素,返回其索引
return m
}
}
// 未找到目标元素,返回 -1
return -1
}
[class]{}-[func]{binarySearch}
```
=== "JavaScript"
@ -153,24 +136,7 @@ $$
=== "Go"
```go title="binary_search.go"
/* 二分查找(左闭右开) */
func binarySearch1(nums []int, target int) int {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
i, j := 0, len(nums)
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
for i < j {
m := (i + j) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j)
i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中
j = m
} else { // 找到目标元素,返回其索引
return m
}
}
// 未找到目标元素,返回 -1
return -1
}
[class]{}-[func]{binarySearch1}
```
=== "JavaScript"

View File

@ -37,16 +37,7 @@ comments: true
=== "Go"
```go title="hashing_search.go"
/* 哈希查找(数组) */
func hashingSearchArray(m map[int]int, target int) int {
// 哈希表的 key: 目标元素value: 索引
// 若哈希表中无此 key ,返回 -1
if index, ok := m[target]; ok {
return index
} else {
return -1
}
}
[class]{}-[func]{hashingSearchArray}
```
=== "JavaScript"
@ -110,16 +101,7 @@ comments: true
=== "Go"
```go title="hashing_search.go"
/* 哈希查找(链表) */
func hashingSearchLinkedList(m map[int]*ListNode, target int) *ListNode {
// 哈希表的 key: 目标结点值value: 结点对象
// 若哈希表中无此 key ,返回 nil
if node, ok := m[target]; ok {
return node
} else {
return nil
}
}
[class]{}-[func]{hashingSearchLinkedList}
```
=== "JavaScript"

View File

@ -33,18 +33,7 @@ comments: true
=== "Go"
```go title="linear_search.go"
/* 线性查找(数组) */
func linearSearchArray(nums []int, target int) int {
// 遍历数组
for i := 0; i < len(nums); i++ {
// 找到目标元素,返回其索引
if nums[i] == target {
return i
}
}
// 未找到目标元素,返回 -1
return -1
}
[class]{}-[func]{linearSearchArray}
```
=== "JavaScript"
@ -106,19 +95,7 @@ comments: true
=== "Go"
```go title="linear_search.go"
/* 线性查找(链表)*/
func linerSearchLinkedList(node *ListNode, target int) *ListNode {
// 遍历链表
for node != nil {
// 找到目标结点,返回之
if node.Val == target {
return node
}
node = node.Next
}
// 未找到目标元素,返回 nil
return nil
}
[class]{}-[func]{linearSearchLinkedList}
```
=== "JavaScript"

View File

@ -68,19 +68,7 @@ comments: true
=== "Go"
```go title="bubble_sort.go"
/* 冒泡排序 */
func bubbleSort(nums []int) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i := len(nums) - 1; i > 0; i-- {
// 内循环:冒泡操作
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j+1] = nums[j+1], nums[j]
}
}
}
}
[class]{}-[func]{bubbleSort}
```
=== "JavaScript"
@ -174,24 +162,7 @@ comments: true
=== "Go"
```go title="bubble_sort.go"
/* 冒泡排序(标志优化)*/
func bubbleSortWithFlag(nums []int) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i := len(nums) - 1; i > 0; i-- {
flag := false // 初始化标志位
// 内循环:冒泡操作
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j+1] = nums[j+1], nums[j]
flag = true // 记录交换元素
}
}
if flag == false { // 此轮冒泡未交换任何元素,直接跳出
break
}
}
}
[class]{}-[func]{bubbleSortWithFlag}
```
=== "JavaScript"

View File

@ -45,20 +45,7 @@ comments: true
=== "Go"
```go title="insertion_sort.go"
/* 插入排序 */
func insertionSort(nums []int) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i := 1; i < len(nums); i++ {
base := nums[i]
j := i - 1
// 内循环:将 base 插入到左边的正确位置
for j >= 0 && nums[j] > base {
nums[j+1] = nums[j] // 1. 将 nums[j] 向右移动一位
j--
}
nums[j+1] = base // 2. 将 base 赋值到正确位置
}
}
[class]{}-[func]{insertionSort}
```
=== "JavaScript"

View File

@ -68,24 +68,7 @@ comments: true
=== "Go"
```go title="quick_sort.go"
/* 哨兵划分 */
func partition(nums []int, left, right int) int {
// 以 nums[left] 作为基准数
i, j := left, right
for i < j {
for i < j && nums[j] >= nums[left] {
j-- // 从右向左找首个小于基准数的元素
}
for i < j && nums[i] <= nums[left] {
i++ // 从左向右找首个大于基准数的元素
}
//元素交换
nums[i], nums[j] = nums[j], nums[i]
}
// 将基准数交换至两子数组的分界线
nums[i], nums[left] = nums[left], nums[i]
return i // 返回基准数的索引
}
[class]{quickSort}-[func]{partition}
```
=== "JavaScript"
@ -169,18 +152,7 @@ comments: true
=== "Go"
```go title="quick_sort.go"
/* 快速排序 */
func quickSort(nums []int, left, right int) {
// 子数组长度为 1 时终止递归
if left >= right {
return
}
// 哨兵划分
pivot := partition(nums, left, right)
// 递归左子数组、右子数组
quickSort(nums, left, pivot-1)
quickSort(nums, pivot+1, right)
}
[class]{quickSort}-[func]{quickSort}
```
=== "JavaScript"
@ -276,25 +248,9 @@ comments: true
=== "Go"
```go title="quick_sort.go"
/* 选取三个元素的中位数 */
func medianThree(nums []int, left, mid, right int) int {
if (nums[left] < nums[mid]) != (nums[left] < nums[right]) {
return left
} else if (nums[mid] > nums[left]) != (nums[mid] > nums[right]) {
return mid
}
return right
}
[class]{quickSortMedian}-[func]{medianThree}
/* 哨兵划分(三数取中值)*/
func partition(nums []int, left, right int) int {
// 以 nums[left] 作为基准数
med := medianThree(nums, left, (left+right)/2, right)
// 将中位数交换至数组最左端
nums[left], nums[med] = nums[med], nums[left]
// 以 nums[left] 作为基准数
// 下同省略...
}
[class]{quickSortMedian}-[func]{partition}
```
=== "JavaScript"
@ -368,22 +324,7 @@ comments: true
=== "Go"
```go title="quick_sort.go"
/* 快速排序(尾递归优化)*/
func quickSort(nums []int, left, right int) {
// 子数组长度为 1 时终止
for left < right {
// 哨兵划分操作
pivot := partition(nums, left, right)
// 对两个子数组中较短的那个执行快排
if pivot-left < right-pivot {
quickSort(nums, left, pivot-1) // 递归排序左子数组
left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right]
} else {
quickSort(nums, pivot+1, right) // 递归排序右子数组
right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1]
}
}
}
[class]{quickSortTailCall}-[func]{quickSort}
```
=== "JavaScript"

View File

@ -302,52 +302,7 @@ comments: true
=== "Go"
```go title="linkedlist_queue.go"
/* 基于链表实现的队列 */
type linkedListQueue struct {
// 使用内置包 list 来实现队列
data *list.List
}
// newLinkedListQueue 初始化链表
func newLinkedListQueue() *linkedListQueue {
return &linkedListQueue{
data: list.New(),
}
}
// push 入队
func (s *linkedListQueue) push(value any) {
s.data.PushBack(value)
}
// poll 出队
func (s *linkedListQueue) poll() any {
if s.isEmpty() {
return nil
}
e := s.data.Front()
s.data.Remove(e)
return e.Value
}
// peek 访问队首元素
func (s *linkedListQueue) peek() any {
if s.isEmpty() {
return nil
}
e := s.data.Front()
return e.Value
}
// size 获取队列的长度
func (s *linkedListQueue) size() int {
return s.data.Len()
}
// isEmpty 判断队列是否为空
func (s *linkedListQueue) isEmpty() bool {
return s.data.Len() == 0
}
[class]{linkedListQueue}-[func]{}
```
=== "JavaScript"
@ -424,74 +379,7 @@ comments: true
=== "Go"
```go title="array_queue.go"
/* 基于环形数组实现的队列 */
type arrayQueue struct {
nums []int // 用于存储队列元素的数组
front int // 队首指针,指向队首元素
queSize int // 队列长度
queCapacity int // 队列容量(即最大容纳元素数量)
}
// newArrayQueue 基于环形数组实现的队列
func newArrayQueue(queCapacity int) *arrayQueue {
return &arrayQueue{
nums: make([]int, queCapacity),
queCapacity: queCapacity,
front: 0,
queSize: 0,
}
}
// size 获取队列的长度
func (q *arrayQueue) size() int {
return q.queSize
}
// isEmpty 判断队列是否为空
func (q *arrayQueue) isEmpty() bool {
return q.queSize == 0
}
// push 入队
func (q *arrayQueue) push(num int) {
// 当 rear == queCapacity 表示队列已满
if q.queSize == q.queCapacity {
return
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
rear := (q.front + q.queSize) % q.queCapacity
// 尾结点后添加 num
q.nums[rear] = num
q.queSize++
}
// poll 出队
func (q *arrayQueue) poll() any {
num := q.peek()
// 队首指针向后移动一位,若越过尾部则返回到数组头部
q.front = (q.front + 1) % q.queCapacity
q.queSize--
return num
}
// peek 访问队首元素
func (q *arrayQueue) peek() any {
if q.isEmpty() {
return nil
}
return q.nums[q.front]
}
// 获取 Slice 用于打印
func (q *arrayQueue) toSlice() []int {
rear := (q.front + q.queSize)
if rear >= q.queCapacity {
rear %= q.queCapacity
return append(q.nums[q.front:], q.nums[:rear]...)
}
return q.nums[q.front:rear]
}
[class]{arrayQueue}-[func]{}
```
=== "JavaScript"

View File

@ -305,52 +305,7 @@ comments: true
=== "Go"
```go title="linkedlist_stack.go"
/* 基于链表实现的栈 */
type linkedListStack struct {
// 使用内置包 list 来实现栈
data *list.List
}
// newLinkedListStack 初始化链表
func newLinkedListStack() *linkedListStack {
return &linkedListStack{
data: list.New(),
}
}
// push 入栈
func (s *linkedListStack) push(value int) {
s.data.PushBack(value)
}
// pop 出栈
func (s *linkedListStack) pop() any {
if s.isEmpty() {
return nil
}
e := s.data.Back()
s.data.Remove(e)
return e.Value
}
// peek 访问栈顶元素
func (s *linkedListStack) peek() any {
if s.isEmpty() {
return nil
}
e := s.data.Back()
return e.Value
}
// size 获取栈的长度
func (s *linkedListStack) size() int {
return s.data.Len()
}
// isEmpty 判断栈是否为空
func (s *linkedListStack) isEmpty() bool {
return s.data.Len() == 0
}
[class]{linkedListStack}-[func]{}
```
=== "JavaScript"
@ -425,53 +380,7 @@ comments: true
=== "Go"
```go title="array_stack.go"
/* 基于数组实现的栈 */
type arrayStack struct {
data []int // 数据
}
func newArrayStack() *arrayStack {
return &arrayStack{
// 设置栈的长度为 0容量为 16
data: make([]int, 0, 16),
}
}
// size 栈的长度
func (s *arrayStack) size() int {
return len(s.data)
}
// isEmpty 栈是否为空
func (s *arrayStack) isEmpty() bool {
return s.size() == 0
}
// push 入栈
func (s *arrayStack) push(v int) {
// 切片会自动扩容
s.data = append(s.data, v)
}
// pop 出栈
func (s *arrayStack) pop() any {
// 弹出栈前,先判断是否为空
if s.isEmpty() {
return nil
}
val := s.peek()
s.data = s.data[:len(s.data)-1]
return val
}
// peek 获取栈顶元素
func (s *arrayStack) peek() any {
if s.isEmpty() {
return nil
}
val := s.data[len(s.data)-1]
return val
}
[class]{arrayStack}-[func]{}
```
=== "JavaScript"

View File

@ -182,26 +182,9 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
=== "Go"
```go title="avl_tree.go"
/* 获取结点高度 */
func height(node *TreeNode) int {
// 空结点高度为 -1 ,叶结点高度为 0
if node != nil {
return node.Height
}
return -1
}
[class]{aVLTree}-[func]{height}
/* 更新结点高度 */
func updateHeight(node *TreeNode) {
lh := height(node.Left)
rh := height(node.Right)
// 结点高度等于最高子树高度 + 1
if lh > rh {
node.Height = lh + 1
} else {
node.Height = rh + 1
}
}
[class]{aVLTree}-[func]{updateHeight}
```
=== "JavaScript"
@ -273,15 +256,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
=== "Go"
```go title="avl_tree.go"
/* 获取平衡因子 */
func balanceFactor(node *TreeNode) int {
// 空结点平衡因子为 0
if node == nil {
return 0
}
// 结点平衡因子 = 左子树高度 - 右子树高度
return height(node.Left) - height(node.Right)
}
[class]{aVLTree}-[func]{balanceFactor}
```
=== "JavaScript"
@ -375,19 +350,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
=== "Go"
```go title="avl_tree.go"
/* 右旋操作 */
func rightRotate(node *TreeNode) *TreeNode {
child := node.Left
grandChild := child.Right
// 以 child 为原点,将 node 向右旋转
child.Right = node
node.Left = grandChild
// 更新结点高度
updateHeight(node)
updateHeight(child)
// 返回旋转后子树的根结点
return child
}
[class]{aVLTree}-[func]{rightRotate}
```
=== "JavaScript"
@ -459,19 +422,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
=== "Go"
```go title="avl_tree.go"
/* 左旋操作 */
func leftRotate(node *TreeNode) *TreeNode {
child := node.Right
grandChild := child.Left
// 以 child 为原点,将 node 向左旋转
child.Left = node
node.Right = grandChild
// 更新结点高度
updateHeight(node)
updateHeight(child)
// 返回旋转后子树的根结点
return child
}
[class]{aVLTree}-[func]{leftRotate}
```
=== "JavaScript"
@ -566,36 +517,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
=== "Go"
```go title="avl_tree.go"
/* 执行旋转操作,使该子树重新恢复平衡 */
func rotate(node *TreeNode) *TreeNode {
// 获取结点 node 的平衡因子
// Go 推荐短变量,这里 bf 指代 balanceFactor
bf := balanceFactor(node)
// 左偏树
if bf > 1 {
if balanceFactor(node.Left) >= 0 {
// 右旋
return rightRotate(node)
} else {
// 先左旋后右旋
node.Left = leftRotate(node.Left)
return rightRotate(node)
}
}
// 右偏树
if bf < -1 {
if balanceFactor(node.Right) <= 0 {
// 左旋
return leftRotate(node)
} else {
// 先右旋后左旋
node.Right = rightRotate(node.Right)
return leftRotate(node)
}
}
// 平衡树,无需旋转,直接返回
return node
}
[class]{aVLTree}-[func]{rotate}
```
=== "JavaScript"
@ -667,32 +589,9 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
=== "Go"
```go title="avl_tree.go"
/* 插入结点 */
func (t *avlTree) insert(val int) *TreeNode {
t.root = insertHelper(t.root, val)
return t.root
}
/* 递归插入结点(辅助函数) */
func insertHelper(node *TreeNode, val int) *TreeNode {
if node == nil {
return NewTreeNode(val)
}
/* 1. 查找插入位置,并插入结点 */
if val < node.Val {
node.Left = insertHelper(node.Left, val)
} else if val > node.Val {
node.Right = insertHelper(node.Right, val)
} else {
// 重复结点不插入,直接返回
return node
}
// 更新结点高度
updateHeight(node)
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node)
// 返回子树的根结点
return node
}
[class]{aVLTree}-[func]{insert}
[class]{aVLTree}-[func]{insertHelper}
```
=== "JavaScript"
@ -778,61 +677,11 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
=== "Go"
```go title="avl_tree.go"
/* 删除结点 */
func (t *avlTree) remove(val int) *TreeNode {
root := removeHelper(t.root, val)
return root
}
[class]{aVLTree}-[func]{remove}
/* 递归删除结点(辅助函数) */
func removeHelper(node *TreeNode, val int) *TreeNode {
if node == nil {
return nil
}
/* 1. 查找结点,并删除之 */
if val < node.Val {
node.Left = removeHelper(node.Left, val)
} else if val > node.Val {
node.Right = removeHelper(node.Right, val)
} else {
if node.Left == nil || node.Right == nil {
child := node.Left
if node.Right != nil {
child = node.Right
}
// 子结点数量 = 0 ,直接删除 node 并返回
if child == nil {
return nil
} else {
// 子结点数量 = 1 ,直接删除 node
node = child
}
} else {
// 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点
temp := getInOrderNext(node.Right)
node.Right = removeHelper(node.Right, temp.Val)
node.Val = temp.Val
}
}
// 更新结点高度
updateHeight(node)
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node)
// 返回子树的根结点
return node
}
[class]{aVLTree}-[func]{removeHelper}
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
func getInOrderNext(node *TreeNode) *TreeNode {
if node == nil {
return node
}
// 循环访问左子结点,直到叶结点时为最小结点,跳出
for node.Left != nil {
node = node.Left
}
return node
}
[class]{aVLTree}-[func]{getInOrderNext}
```
=== "JavaScript"

View File

@ -56,25 +56,7 @@ comments: true
=== "Go"
```go title="binary_search_tree.go"
/* 查找结点 */
func (bst *binarySearchTree) search(num int) *TreeNode {
node := bst.root
// 循环查找,越过叶结点后跳出
for node != nil {
if node.Val < num {
// 目标结点在 cur 的右子树中
node = node.Right
} else if node.Val > num {
// 目标结点在 cur 的左子树中
node = node.Left
} else {
// 找到目标结点,跳出循环
break
}
}
// 返回目标结点
return node
}
[class]{binarySearchTree}-[func]{search}
```
=== "JavaScript"
@ -145,36 +127,7 @@ comments: true
=== "Go"
```go title="binary_search_tree.go"
/* 插入结点 */
func (bst *binarySearchTree) insert(num int) *TreeNode {
cur := bst.root
// 若树为空,直接提前返回
if cur == nil {
return nil
}
// 待插入结点之前的结点位置
var pre *TreeNode = nil
// 循环查找,越过叶结点后跳出
for cur != nil {
if cur.Val == num {
return nil
}
pre = cur
if cur.Val < num {
cur = cur.Right
} else {
cur = cur.Left
}
}
// 插入结点
node := NewTreeNode(num)
if pre.Val < num {
pre.Right = node
} else {
pre.Left = node
}
return cur
}
[class]{binarySearchTree}-[func]{insert}
```
=== "JavaScript"
@ -276,72 +229,9 @@ comments: true
=== "Go"
```go title="binary_search_tree.go"
/* 删除结点 */
func (bst *binarySearchTree) remove(num int) *TreeNode {
cur := bst.root
// 若树为空,直接提前返回
if cur == nil {
return nil
}
// 待删除结点之前的结点位置
var pre *TreeNode = nil
// 循环查找,越过叶结点后跳出
for cur != nil {
if cur.Val == num {
break
}
pre = cur
if cur.Val < num {
// 待删除结点在右子树中
cur = cur.Right
} else {
// 待删除结点在左子树中
cur = cur.Left
}
}
// 若无待删除结点,则直接返回
if cur == nil {
return nil
}
// 子结点数为 0 或 1
if cur.Left == nil || cur.Right == nil {
var child *TreeNode = nil
// 取出待删除结点的子结点
if cur.Left != nil {
child = cur.Left
} else {
child = cur.Right
}
// 将子结点替换为待删除结点
if pre.Left == cur {
pre.Left = child
} else {
pre.Right = child
}
// 子结点数为 2
} else {
// 获取中序遍历中待删除结点 cur 的下一个结点
next := bst.getInOrderNext(cur)
temp := next.Val
// 递归删除结点 next
bst.remove(next.Val)
// 将 next 的值复制给 cur
cur.Val = temp
}
return cur
}
[class]{binarySearchTree}-[func]{remove}
/* 获取中序遍历的下一个结点(仅适用于 root 有左子结点的情况) */
func (bst *binarySearchTree) getInOrderNext(node *TreeNode) *TreeNode {
if node == nil {
return node
}
// 循环访问左子结点,直到叶结点时为最小结点,跳出
for node.Left != nil {
node = node.Left
}
return node
}
[class]{binarySearchTree}-[func]{getInOrderNext}
```
=== "JavaScript"

View File

@ -39,29 +39,7 @@ comments: true
=== "Go"
```go title="binary_tree_bfs.go"
/* 层序遍历 */
func levelOrder(root *TreeNode) []int {
// 初始化队列,加入根结点
queue := list.New()
queue.PushBack(root)
// 初始化一个切片,用于保存遍历序列
nums := make([]int, 0)
for queue.Len() > 0 {
// poll
node := queue.Remove(queue.Front()).(*TreeNode)
// 保存结点值
nums = append(nums, node.Val)
if node.Left != nil {
// 左子结点入队
queue.PushBack(node.Left)
}
if node.Right != nil {
// 右子结点入队
queue.PushBack(node.Right)
}
}
return nums
}
[class]{}-[func]{hierOrder}
```
=== "JavaScript"
@ -153,38 +131,11 @@ comments: true
=== "Go"
```go title="binary_tree_dfs.go"
/* 前序遍历 */
func preOrder(node *TreeNode) {
if node == nil {
return
}
// 访问优先级:根结点 -> 左子树 -> 右子树
nums = append(nums, node.Val)
preOrder(node.Left)
preOrder(node.Right)
}
[class]{}-[func]{preOrder}
/* 中序遍历 */
func inOrder(node *TreeNode) {
if node == nil {
return
}
// 访问优先级:左子树 -> 根结点 -> 右子树
inOrder(node.Left)
nums = append(nums, node.Val)
inOrder(node.Right)
}
[class]{}-[func]{inOrder}
/* 后序遍历 */
func postOrder(node *TreeNode) {
if node == nil {
return
}
// 访问优先级:左子树 -> 右子树 -> 根结点
postOrder(node.Left)
postOrder(node.Right)
nums = append(nums, node.Val)
}
[class]{}-[func]{postOrder}
```
=== "JavaScript"

View File

@ -16,6 +16,7 @@ from docs.utils.extract_code_cpp import ExtractCodeBlocksCpp
from docs.utils.extract_code_jsts import ExtractCodeBlocksJSTS
from docs.utils.extract_code_swift import ExtractCodeBlocksSwift
from docs.utils.extract_code_csharp import ExtractCodeBlocksCSharp
from docs.utils.extract_code_go import ExtractCodeBlocksGo
def build_markdown(md_path):
@ -92,6 +93,7 @@ extractor_dict = {
"java": ExtractCodeBlocksJava(),
"python": ExtractCodeBlocksPython(),
"cpp": ExtractCodeBlocksCpp(),
"go": ExtractCodeBlocksGo(),
"javascript": ExtractCodeBlocksJSTS(),
"typescript": ExtractCodeBlocksJSTS(),
"swift": ExtractCodeBlocksSwift(),

View File

@ -0,0 +1,183 @@
"""
File: extract_code_go.py
Created Time: 2023-02-07
Author: Krahets (krahets@163.com)
"""
import re
import glob
import sys, os.path as osp
sys.path.append(osp.dirname(osp.dirname(osp.dirname(osp.abspath(__file__)))))
from docs.utils.extract_code_java import ExtractCodeBlocksJava
class ExtractCodeBlocksGo(ExtractCodeBlocksJava):
def __init__(self) -> None:
super().__init__()
# Pattern to match function names and class names
self.func_pattern = r'(\s*)func\s+(\(.+\))*\s*(\w+)\((.*)\)\s*(\S*)\s*{\n'
self.class_pattern = r'(\s*)type\s+(\w+)'
self.func_pattern_keys = ["total", "ind", "class", "label", "params", "return"]
self.class_pattern_keys = ["total", "ind", "label"]
# Pattern to match the start and end of a block
self.block_end_pattern = '^\s{ind}\}'
self.block_start_pattern = '^\s{ind}\/\*.+\*\/'
self.block_start_shift = 0
self.block_end_shift = 0
def extract(self, file_path):
"""
Extract classes and functions from a markdown document
"""
if not osp.isfile(file_path):
return None
self.file_path = file_path
with open(file_path) as f:
self.lines = f.readlines()
self.content = "".join(self.lines)
# Detect and extract all the classes and fucntions
classes = self.extract_class_blocks()
funcs = self.extract_function_blocks(classes=classes)
self.post_process(classes, funcs)
return {
"classes": classes,
"funcs": funcs,
}
def extract_function_blocks(self, classes=None, class_label="", indentation=0):
"""
Extract all the functions with given indentation
"""
funcs = {}
func_pattern = re.compile(self.func_pattern)
for line_num in range(len(self.lines)):
# Search the function header
func_match = func_pattern.match(self.lines[line_num])
if func_match is None:
continue
header_line = line_num
func_label = func_match.group(self.func_pattern_keys.index("label"))
func_cls_label = func_match.group(self.func_pattern_keys.index("class"))
func_return = func_match.group(self.func_pattern_keys.index("return"))
if classes:
# The function should not blong to any class
flag = False
for label in classes:
# Match the target class label
class_label_pattern = re.compile(f".*\*{label}\).*")
func_return_pattern = re.compile(f".*\*{label}.*")
constructor_pattern = re.compile(f".*new.*")
class_label_match = class_label_pattern.match(f"{func_cls_label}")
func_return_match = func_return_pattern.match(f"{func_return}")
constructor_match = constructor_pattern.match(func_label)
if class_label_match is not None or \
func_return_match is not None and constructor_match is not None:
flag = True
if flag:
continue
elif class_label:
# Match the target class label
class_label_pattern = re.compile(f".*\*{class_label}\).*")
func_return_pattern = re.compile(f".*\*{class_label}.*")
constructor_pattern = re.compile(f".*new.*")
class_label_match = class_label_pattern.match(f"{func_cls_label}")
func_return_match = func_return_pattern.match(f"{func_return}")
constructor_match = constructor_pattern.match(func_label)
if class_label_match is None and func_return_match is None:
continue
if func_return_match is not None and constructor_match is None:
continue
# Search the block from the header line
start_line, end_line, func_block = self.search_block(
header_line, indentation)
# Construct the funcs dict
funcs[func_label] = {
"indentation": indentation,
"line_number": {
"start": start_line,
"end": end_line,
"header": header_line,
},
"block": func_block,
}
return funcs
def extract_class_blocks(self):
"""
Extract all the classes with given indentation
"""
classes = {}
class_pattern = re.compile(self.class_pattern)
for line_num, line in enumerate(self.lines):
# Search the class header
class_match = class_pattern.match(line)
if class_match is None:
continue
header_line = line_num
# Search the block from the header line
_, _, class_block = self.search_block(
header_line, 0)
# Construct the classes dict
class_label = class_match.group(self.class_pattern_keys.index("label"))
funcs = self.extract_function_blocks(class_label=class_label)
# Merge function blocks to class_block
for func in funcs.values():
class_block.append('\n')
class_block += func["block"]
classes[class_label] = {
"indentation": 0,
"line_number": {
"header": header_line,
},
"block": class_block,
"funcs": funcs,
}
return classes
def post_process(self, classes, funcs):
"""
Process the classes and functions
"""
def replace_tabs(x):
for i, line in enumerate(x["block"]):
x["block"][i] = line.replace("\t"," " * self.ind)
def add_inds(x):
for i, line in enumerate(x["block"]):
if line != "\n":
x["block"][i] = " " * self.ind + line
for clas in classes.values():
replace_tabs(clas)
for func in clas["funcs"].values():
replace_tabs(func)
add_inds(func)
for func in funcs.values():
replace_tabs(func)
for code_path in glob.glob("codes/go/chapter_*/array_hash_map.go"):
ext = ExtractCodeBlocksGo()
res = ext.extract(code_path)
pass

View File

@ -119,7 +119,6 @@ class ExtractCodeBlocksJava:
Extract all the classes with given indentation
"""
classes = {}
class_pattern = re.compile(self.class_pattern)
for line_num, line in enumerate(self.lines):