Merge branch 'master' into master

pull/253/head
Yudong Jin 2 years ago committed by GitHub
commit a0990a0f7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

2
.gitattributes vendored

@ -3,12 +3,12 @@
*.py linguist-language=Python
*.cpp linguist-language=C++
*.c linguist-language=C++
*.zig linguist-language=C++
*.go linguist-language=Go
*.js linguist-language=JavaScript
*.ts linguist-language=JavaScript
*.swift linguist-language=Swift
*.zig linguist-language=Other
*.rs linguist-language=Other
*.html linguist-detectable=false

@ -11,3 +11,4 @@ add_subdirectory(chapter_array_and_linkedlist)
add_subdirectory(chapter_sorting)
add_subdirectory(chapter_tree)
add_subdirectory(chapter_stack_and_queue)
add_subdirectory(chapter_heap)

@ -0,0 +1,2 @@
add_executable(my_heap my_heap.c)

@ -0,0 +1,185 @@
/**
* File: my_heap.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "../include/include.h"
#define MAX_SIZE 5000
// 大顶堆
typedef struct maxHeap {
// size 代表的是实际元素的个数
int size;
// 使用预先分配内存的数组,避免扩容
int data[MAX_SIZE];
} maxHeap;
void siftDown(maxHeap *h, int i);
void siftUp(maxHeap *h, int i);
/* 构造空堆 */
maxHeap *newEmptyMaxHeap() {
// 所有元素入堆
maxHeap *h = (maxHeap *) malloc(sizeof(maxHeap));
h->size = 0;
return h;
}
/* 构造函数,根据切片建堆 */
maxHeap *newMaxHeap(int nums[], int size) {
// 所有元素入堆
maxHeap *h = (maxHeap *) malloc(sizeof(maxHeap));
h->size = size;
memcpy(h->data, nums, size * sizeof(int));
for (int i = size - 1; i >= 0; i--) {
// 堆化除叶结点以外的其他所有结点
siftDown(h, i);
}
return h;
}
/* 获取左子结点索引 */
int left(maxHeap *h, int i) {
return 2 * i + 1;
}
/* 获取右子结点索引 */
int right(maxHeap *h, int i) {
return 2 * i + 2;
}
/* 获取父结点索引 */
int parent(maxHeap *h, int i) {
return (i - 1) / 2;
}
/* 交换元素 */
int swap(maxHeap *h, int i, int j) {
int temp = h->data[i];
h->data[i] = h->data[j];
h->data[j] = temp;
}
/* 获取堆大小 */
int size(maxHeap *h) {
return h->size;
}
/* 判断堆是否为空 */
int isEmpty(maxHeap *h) {
return h->size == 0;
}
/* 访问堆顶元素 */
int peek(maxHeap *h) {
return h->data[0];
}
/* 元素入堆 */
int push(maxHeap *h, int val) {
// 默认情况下,不应该添加这么多结点
if (h->size == MAX_SIZE) {
printf("heap is full!");
return NIL;
}
// 添加结点
h->data[h->size] = val;
h->size++;
// 从底至顶堆化
siftUp(h, h->size - 1);
}
/* 元素出堆 */
int poll(maxHeap *h) {
// 判空处理
if (isEmpty(h)) {
printf("heap is empty!");
return NIL;
}
// 交换根结点与最右叶结点(即交换首元素与尾元素)
swap(h, 0, size(h) - 1);
// 删除结点
int val = h->data[h->size - 1];
h->size--;
// 从顶至底堆化
siftDown(h, 0);
// 返回堆顶元素
return val;
}
/* 从结点 i 开始,从顶至底堆化 */
void siftDown(maxHeap *h, int i) {
while (true) {
// 判断结点 i, l, r 中值最大的结点,记为 max
int l = left(h, i);
int r = right(h, i);
int max = i;
if (l < size(h) && h->data[l] > h->data[max]) {
max = l;
}
if (r < size(h) && h->data[r] > h->data[max]) {
max = r;
}
// 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出
if (max == i) {
break;
}
// 交换两结点
swap(h, i, max);
// 循环向下堆化
i = max;
}
}
/* 从结点 i 开始,从底至顶堆化 */
void siftUp(maxHeap *h, int i) {
while (true) {
// 获取结点 i 的父结点
int p = parent(h, i);
// 当“越过根结点”或“结点无需修复”时,结束堆化
if (p < 0 || h->data[i] <= h->data[p]) {
break;
}
// 交换两结点
swap(h, i, p);
// 循环向上堆化
i = p;
}
}
int main() {
/* 初始化堆 */
// 初始化大顶堆
int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2};
maxHeap *heap = newMaxHeap(nums, sizeof(nums) / sizeof(int));
printf("输入数组并建堆后\n");
printHeap(heap->data, heap->size);
/* 获取堆顶元素 */
printf("\n堆顶元素为 %d\n", peek(heap));
/* 元素入堆 */
push(heap, 7);
printf("\n元素 7 入堆后\n");
printHeap(heap->data, heap->size);
/* 堆顶元素出堆 */
int top = poll(heap);
printf("\n堆顶元素 %d 出堆后\n", top);
printHeap(heap->data, heap->size);
/* 获取堆大小 */
printf("\n堆元素数量为 %d\n", size(heap));
/* 判断堆是否为空 */
printf("\n堆是否为空 %d\n", isEmpty(heap));
// 释放内存
free(heap);
}

@ -1,3 +1,4 @@
add_executable(avl_tree avl_tree.c)
add_executable(binary_search binary_tree.c)
add_executable(binary_tree_bfs binary_tree_bfs.c)
add_executable(binary_tree_dfs binary_tree_dfs.c)

@ -0,0 +1,261 @@
/**
* File: avl_tree.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "../include/include.h"
/* AVL Tree */
struct avlTree {
TreeNode *root;
};
typedef struct avlTree avlTree;
/* 构建 AVL 树 */
avlTree *newAVLTree() {
avlTree *tree = (avlTree *) malloc(sizeof(avlTree));
tree->root = NULL;
return tree;
}
int height(TreeNode *node) {
// 空结点高度为 -1 ,叶结点高度为 0
if (node != NULL) {
return node->height;
}
return -1;
}
/* 更新结点高度 */
int updateHeight(TreeNode *node) {
int lh = height(node->left);
int rh = height(node->right);
// 结点高度等于最高子树高度 + 1
if (lh > rh) {
node->height = lh + 1;
} else {
node->height = rh + 1;
}
}
/* 获取平衡因子 */
int balanceFactor(TreeNode *node) {
// 空结点平衡因子为 0
if (node == NULL) {
return 0;
}
// 结点平衡因子 = 左子树高度 - 右子树高度
return height(node->left) - height(node->right);
}
/* 右旋操作 */
TreeNode *rightRotate(TreeNode *node) {
TreeNode *child, *grandChild;
child = node->left;
grandChild = child->right;
// 以 child 为原点,将 node 向右旋转
child->right = node;
node->left = grandChild;
// 更新结点高度
updateHeight(node);
updateHeight(child);
// 返回旋转后子树的根节点
return child;
}
/* 左旋操作 */
TreeNode *leftRotate(TreeNode *node) {
TreeNode *child, *grandChild;
child = node->right;
grandChild = child->left;
// 以 child 为原点,将 node 向左旋转
child->left = node;
node->right = grandChild;
// 更新结点高度
updateHeight(node);
updateHeight(child);
// 返回旋转后子树的根节点
return child;
}
/* 执行旋转操作,使该子树重新恢复平衡 */
TreeNode *rotate(TreeNode *node) {
// 获取结点 node 的平衡因子
int 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;
}
/* 递归插入结点(辅助函数) */
TreeNode *insertHelper(TreeNode *node, int val) {
if (node == NULL) {
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;
}
/* 插入结点 */
TreeNode *insert(avlTree *tree, int val) {
tree->root = insertHelper(tree->root, val);
return tree->root;
}
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
TreeNode *getInOrderNext(TreeNode *node) {
if (node == NULL) {
return node;
}
// 循环访问左子结点,直到叶结点时为最小结点,跳出
while (node->left != NULL) {
node = node->left;
}
return node;
}
/* 递归删除结点(辅助函数) */
TreeNode *removeHelper(TreeNode *node, int val) {
TreeNode *child, *grandChild, *temp;
if (node == NULL) {
return NULL;
}
/* 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 == NULL || node->right == NULL) {
child = node->left;
if (node->right != NULL) {
child = node->right;
}
// 子结点数量 = 0 ,直接删除 node 并返回
if (child == NULL) {
return NULL;
} 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;
}
/* 删除结点 */
// 由于引入了 stdio.h ,此处无法使用 remove 关键词
TreeNode *removeNode(avlTree *tree, int val) {
TreeNode *root = removeHelper(tree->root, val);
return root;
}
/* 查找结点 */
TreeNode *search(avlTree *tree, int val) {
TreeNode *cur = tree->root;
// 循环查找,越过叶结点后跳出
while (cur != NULL) {
if (cur->val < val) {
// 目标结点在 cur 的右子树中
cur = cur->right;
} else if (cur->val > val) {
// 目标结点在 cur 的左子树中
cur = cur->left;
} else {
// 找到目标结点,跳出循环
break;
}
}
// 找到目标结点,跳出循环
return cur;
}
void testInsert(avlTree *tree, int val) {
insert(tree, val);
printf("\n插入结点 %d 后AVL 树为 \n", val);
printTree(tree->root);
}
void testRemove(avlTree *tree, int val) {
removeNode(tree, val);
printf("\n删除结点 %d 后AVL 树为 \n", val);
printTree(tree->root);
}
/* Driver Code */
int main() {
/* 初始化空 AVL 树 */
avlTree *tree = (avlTree *) newAVLTree();
/* 插入结点 */
// 请关注插入结点后AVL 树是如何保持平衡的
testInsert(tree, 1);
testInsert(tree, 2);
testInsert(tree, 3);
testInsert(tree, 4);
testInsert(tree, 5);
testInsert(tree, 8);
testInsert(tree, 7);
testInsert(tree, 9);
testInsert(tree, 10);
testInsert(tree, 6);
/* 插入重复结点 */
testInsert(tree, 7);
/* 删除结点 */
// 请关注删除结点后AVL 树是如何保持平衡的
testRemove(tree, 8); // 删除度为 0 的结点
testRemove(tree, 5); // 删除度为 1 的结点
testRemove(tree, 4); // 删除度为 2 的结点
/* 查询结点 */
TreeNode *node = search(tree, 7);
printf("\n查找到的结点对象结点值 = %d \n", node->val);
}

@ -6,6 +6,187 @@
#include "../include/include.h"
/* 二叉搜索树 */
struct binarySearchTree {
TreeNode *root;
};
typedef struct binarySearchTree binarySearchTree;
int sortIntHelper(const void *a, const void *b) {
// 从小到大排序
return (*(int *) a - *(int *) b);
}
/* 构建二叉搜索树 */
TreeNode *buildTree(int nums[], int i, int j) {
if (i > j) {
return NULL;
}
// 将数组中间结点作为根结点
int mid = (i + j) / 2;
TreeNode *root = newTreeNode(nums[mid]);
// 递归建立左子树和右子树
root->left = buildTree(nums, i, mid - 1);
root->right = buildTree(nums, mid + 1, j);
return root;
}
binarySearchTree *newBinarySearchTree(int nums[], int size) {
binarySearchTree *bst = (binarySearchTree *) malloc(sizeof(binarySearchTree));
TreeNode *root;
// 从小到大排序数组
qsort(nums, size, sizeof(int), sortIntHelper);
// 构建二叉搜索树
root = buildTree(nums, 0, size - 1);
bst->root = root;
return bst;
}
/* 获取二叉树根结点 */
TreeNode *getRoot(binarySearchTree *bst) {
return bst->root;
}
/* 查找结点 */
TreeNode *search(binarySearchTree *bst, int num) {
TreeNode *cur = bst->root;
// 循环查找,越过叶结点后跳出
while (cur != NULL) {
if (cur->val < num) {
// 目标结点在 cur 的右子树中
cur = cur->right;
} else if (cur->val > num) {
// 目标结点在 cur 的左子树中
cur = cur->left;
} else {
// 找到目标结点,跳出循环
break;
}
}
// 返回目标结点
return cur;
}
/* 插入结点 */
TreeNode *insert(binarySearchTree *bst, int num) {
// 若树为空,直接提前返回
if (bst->root == NULL) return NULL;
TreeNode *cur = bst->root, *pre = NULL;
// 循环查找,越过叶结点后跳出
while (cur != NULL) {
// 找到重复结点,直接返回
if (cur->val == num) {
return NULL;
}
pre = cur;
if (cur->val < num) {
// 插入位置在 cur 的右子树中
cur = cur->right;
} else {
// 插入位置在 cur 的左子树中
cur = cur->left;
}
}
// 插入结点 val
TreeNode *node = newTreeNode(num);
if (pre->val < num) {
pre->right = node;
} else {
pre->left = node;
}
return node;
}
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
TreeNode *getInOrderNext(TreeNode *root) {
if (root == NULL) return root;
// 循环访问左子结点,直到叶结点时为最小结点,跳出
while (root->left != NULL) {
root = root->left;
}
return root;
}
/* 删除结点 */
// 由于引入了 stdio.h ,此处无法使用 remove 关键词
TreeNode *removeNode(binarySearchTree *bst, int num) {
// 若树为空,直接提前返回
if (bst->root == NULL) return NULL;
TreeNode *cur = bst->root, *pre = NULL;
// 循环查找,越过叶结点后跳出
while (cur != NULL) {
// 找到待删除结点,跳出循环
if (cur->val == num) break;
pre = cur;
if (cur->val < num) {
// 待删除结点在 root 的右子树中
cur = cur->right;
} else {
// 待删除结点在 root 的左子树中
cur = cur->left;
}
}
// 若无待删除结点,则直接返回
if (cur == NULL) {
return NULL;
}
// 判断待删除结点是否存在子结点
if (cur->left == NULL || cur->right == NULL) {
/* 子结点数量 = 0 or 1 */
// 当子结点数量 = 0 / 1 时, child = nullptr / 该子结点
TreeNode *child = cur->left != NULL ? cur->left : cur->right;
// 删除结点 cur
if (pre->left == cur) {
pre->left = child;
} else {
pre->right = child;
}
} else {
/* 子结点数量 = 2 */
// 获取中序遍历中 cur 的下一个结点
TreeNode *nex = getInOrderNext(cur->right);
int tmp = nex->val;
// 递归删除结点 nex
removeNode(bst, nex->val);
// 将 nex 的值复制给 cur
cur->val = tmp;
}
return cur;
}
/* Driver Code */
int main() {
/* 初始化二叉搜索树 */
int nums[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
binarySearchTree *bst = newBinarySearchTree(nums, sizeof(nums) / sizeof(int));
printf("初始化的二叉树为\n");
printTree(getRoot(bst));
/* 查找结点 */
TreeNode *node = search(bst, 7);
printf("查找到的结点对象的结点值 = %d\n", node->val);
/* 插入结点 */
insert(bst, 16);
printf("插入结点 16 后,二叉树为\n");
printTree(getRoot(bst));
/* 删除结点 */
removeNode(bst, 1);
printf("删除结点 1 后,二叉树为\n");
printTree(getRoot(bst));
removeNode(bst, 2);
printf("删除结点 2 后,二叉树为\n");
printTree(getRoot(bst));
removeNode(bst, 4);
printf("删除结点 4 后,二叉树为\n");
printTree(getRoot(bst));
// 释放内存
free(bst);
return 0;
}

@ -25,7 +25,7 @@ extern "C" {
* @param arr
* @param size
*/
static void printArray(int *arr, int size) {
static void printArray(int arr[], int size) {
printf("[");
for (int i = 0; i < size - 1; i++) {
if (arr[i] != NIL) {
@ -36,7 +36,7 @@ static void printArray(int *arr, int size) {
}
if (arr[size - 1] != NIL) {
printf("%d]\n", arr[size - 1]);
}else{
} else {
printf("NULL]\n");
}
}
@ -127,6 +127,21 @@ static void printTree(TreeNode *root) {
printTreeHelper(root, NULL, false);
}
/**
* @brief Print a Heap
*
* @param arr
* @param size
*/
static void printHeap(int arr[], int size) {
TreeNode * root;
printf("堆的数组表示:");
printArray(arr, size);
printf("堆的树状表示:\n");
root = arrToTree(arr, size);
printTree(root);
}
#ifdef __cplusplus
}

@ -43,9 +43,9 @@ public:
TreeNode* cur = root;
// 循环查找,越过叶结点后跳出
while (cur != nullptr) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur->val < num) cur = cur->right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur->val > num) cur = cur->left;
// 找到目标结点,跳出循环
else break;
@ -64,9 +64,9 @@ public:
// 找到重复结点,直接返回
if (cur->val == num) return nullptr;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur->val < num) cur = cur->right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur->left;
}
// 插入结点 val

@ -11,8 +11,10 @@ namespace hello_algo.chapter_array_and_linkedlist
/* 随机返回一个数组元素 */
public static int RandomAccess(int[] nums)
{
Random random = new();
Random random=new();
// 在区间 [0, nums.Length) 中随机抽取一个数字
int randomIndex = random.Next(nums.Length);
// 获取并返回随机元素
int randomNum = nums[randomIndex];
return randomNum;
}

@ -193,10 +193,10 @@ namespace hello_algo.chapter_tree
// 循环查找,越过叶结点后跳出
while (cur != null)
{
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < val)
cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > val)
cur = cur.left;
// 找到目标结点,跳出循环

@ -42,9 +42,9 @@ namespace hello_algo.chapter_tree
// 循环查找,越过叶结点后跳出
while (cur != null)
{
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > num) cur = cur.left;
// 找到目标结点,跳出循环
else break;
@ -65,9 +65,9 @@ namespace hello_algo.chapter_tree
// 找到重复结点,直接返回
if (cur.val == num) return null;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur.left;
}

@ -11,7 +11,7 @@ import (
. "github.com/krahets/hello-algo/pkg"
)
func TestLikedList(t *testing.T) {
func TestLinkedList(t *testing.T) {
/* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */
// 初始化各个结点
n0 := NewListNode(1)

@ -195,11 +195,11 @@ func (t *avlTree) search(val int) *TreeNode {
cur := t.root
// 循环查找,越过叶结点后跳出
for cur != nil {
// 目标结点在 root 的右子树中
if cur.Val < val {
// 目标结点在 cur 的右子树中
cur = cur.Right
} else if cur.Val > val {
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
cur = cur.Left
} else {
// 找到目标结点,跳出循环

@ -49,6 +49,6 @@ func testInsert(tree *avlTree, val int) {
func testRemove(tree *avlTree, val int) {
tree.remove(val)
fmt.Printf("\n删除结点 %d 后AVL 树为 \n", val)
fmt.Printf("\n删除结点 %d 后AVL 树为 \n", val)
PrintTree(tree.root)
}

@ -46,10 +46,10 @@ func (bst *binarySearchTree) search(num int) *TreeNode {
// 循环查找,越过叶结点后跳出
for node != nil {
if node.Val < num {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
node = node.Right
} else if node.Val > num {
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
node = node.Left
} else {
// 找到目标结点,跳出循环

@ -0,0 +1,67 @@
/**
* File: radix_sort.java
* Created Time: 2023-01-17
* Author: Krahets (krahets@163.com)
*/
package chapter_sorting;
import java.util.*;
public class radix_sort {
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
static int digit(int num, int exp) {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num / exp) % 10;
}
/* 计数排序(根据 nums 第 k 位排序) */
static void countSort(int[] nums, int exp) {
// 十进制的各位数字范围为 0~9 ,因此需要长度为 10 的桶
int[] bucket = new int[10];
int n = nums.length;
// 借助桶来统计 0~9 各数字的出现次数
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
bucket[d]++; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (int i = 1; i < 10; i++) {
bucket[i] += bucket[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入暂存数组 tmp
int[] tmp = new int[n];
for (int i = n - 1; i >= 0; i--) {
int d = digit(nums[i], exp);
int j = bucket[d] - 1; // 获取 d 在数组中的索引 j
tmp[j] = nums[i]; // 将当前元素填入索引 j
bucket[d]--; // 将 d 的数量减 1
}
// 将 tmp 复制到 nums
for (int i = 0; i < n; i++)
nums[i] = tmp[i];
}
/* 基数排序 */
static void radixSort(int[] nums) {
// 获取数组的最大元素,用于判断最大位数
int ma = Integer.MIN_VALUE;
for (int num : nums)
if (num > ma) ma = num;
// 按照从低位到高位的顺序遍历
for (int exp = 1; ma >= exp; exp *= 10)
// 对数组元素的第 k 位执行「计数排序」
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// k = 3 -> exp = 100
// 即 exp = 10^(k-1)
countSort(nums, exp);
}
public static void main(String[] args) {
/* 基数排序 */
int[] nums = { 23, 12, 3, 4, 788, 192 };
radixSort(nums);
System.out.println("基数排序完成后 nums = " + Arrays.toString(nums));
}
}

@ -165,10 +165,10 @@ class AVLTree {
TreeNode cur = root;
// 循环查找,越过叶结点后跳出
while (cur != null) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < val)
cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > val)
cur = cur.left;
// 找到目标结点,跳出循环

@ -40,9 +40,9 @@ class BinarySearchTree {
TreeNode cur = root;
// 循环查找,越过叶结点后跳出
while (cur != null) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > num) cur = cur.left;
// 找到目标结点,跳出循环
else break;
@ -61,9 +61,9 @@ class BinarySearchTree {
// 找到重复结点,直接返回
if (cur.val == num) return null;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur.left;
}
// 插入结点 val

@ -37,9 +37,9 @@ function search(num) {
let cur = root;
// 循环查找,越过叶结点后跳出
while (cur !== null) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > num) cur = cur.left;
// 找到目标结点,跳出循环
else break;
@ -58,9 +58,9 @@ function insert(num) {
// 找到重复结点,直接返回
if (cur.val === num) return null;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur.left;
}
// 插入结点 val

@ -10,8 +10,8 @@ from include import *
""" 随机访问元素 """
def randomAccess(nums):
# 在区间 [0, len(nums)) 中随机抽取一个数字
random_index = random.randint(0, len(nums))
# 在区间 [0, len(nums)-1] 中随机抽取一个数字
random_index = random.randint(0, len(nums) - 1)
# 获取并返回随机元素
random_num = nums[random_index]
return random_num

@ -12,7 +12,7 @@ from include import *
def bubble_sort(nums):
n = len(nums)
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i in range(n - 1, -1, -1):
for i in range(n - 1, 0, -1):
# 内循环:冒泡操作
for j in range(i):
if nums[j] > nums[j + 1]:
@ -23,7 +23,7 @@ def bubble_sort(nums):
def bubble_sort_with_flag(nums):
n = len(nums)
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i in range(n - 1, -1, -1):
for i in range(n - 1, 0, -1):
flag = False # 初始化标志位
# 内循环:冒泡操作
for j in range(i):

@ -152,10 +152,10 @@ class AVLTree:
cur = self.root
# 循环查找,越过叶结点后跳出
while cur is not None:
# 目标结点在 root 的右子树中
# 目标结点在 cur 的右子树中
if cur.val < val:
cur = cur.right
# 目标结点在 root 的左子树中
# 目标结点在 cur 的左子树中
elif cur.val > val:
cur = cur.left
# 找到目标结点,跳出循环

@ -37,10 +37,10 @@ class BinarySearchTree:
cur = self.root
# 循环查找,越过叶结点后跳出
while cur is not None:
# 目标结点在 root 的右子树中
# 目标结点在 cur 的右子树中
if cur.val < num:
cur = cur.right
# 目标结点在 root 的左子树中
# 目标结点在 cur 的左子树中
elif cur.val > num:
cur = cur.left
# 找到目标结点,跳出循环
@ -64,10 +64,11 @@ class BinarySearchTree:
if cur.val == num:
return None
pre = cur
if cur.val < num: # 插入位置在 root 的右子树中
# 插入位置在 cur 的右子树中
if cur.val < num:
cur = cur.right
else: # 插入位置在 root 的左子树中
# 插入位置在 cur 的左子树中
else:
cur = cur.left
# 插入结点 val

@ -20,6 +20,8 @@ let package = Package(
.executable(name: "linkedlist_queue", targets: ["linkedlist_queue"]),
.executable(name: "array_queue", targets: ["array_queue"]),
.executable(name: "deque", targets: ["deque"]),
.executable(name: "hash_map", targets: ["hash_map"]),
.executable(name: "array_hash_map", targets: ["array_hash_map"]),
],
targets: [
.target(name: "utils", path: "utils"),
@ -38,5 +40,7 @@ let package = Package(
.executableTarget(name: "linkedlist_queue", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_queue.swift"]),
.executableTarget(name: "array_queue", path: "chapter_stack_and_queue", sources: ["array_queue.swift"]),
.executableTarget(name: "deque", path: "chapter_stack_and_queue", sources: ["deque.swift"]),
.executableTarget(name: "hash_map", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map.swift"]),
.executableTarget(name: "array_hash_map", path: "chapter_hashing", sources: ["array_hash_map.swift"]),
]
)

@ -0,0 +1,139 @@
/**
* File: array_hash_map.swift
* Created Time: 2023-01-16
* Author: nuomi1 (nuomi1@qq.com)
*/
/* int->String */
class Entry {
var key: Int
var val: String
init(key: Int, val: String) {
self.key = key
self.val = val
}
}
/* */
class ArrayHashMap {
private var bucket: [Entry?] = []
init() {
// 100
for _ in 0 ..< 100 {
bucket.append(nil)
}
}
/* */
private func hashFunc(key: Int) -> Int {
let index = key % 100
return index
}
/* */
func get(key: Int) -> String? {
let index = hashFunc(key: key)
let pair = bucket[index]
return pair?.val
}
/* */
func put(key: Int, val: String) {
let pair = Entry(key: key, val: val)
let index = hashFunc(key: key)
bucket[index] = pair
}
/* */
func remove(key: Int) {
let index = hashFunc(key: key)
// nil
bucket[index] = nil
}
/* */
func entrySet() -> [Entry] {
var entrySet: [Entry] = []
for pair in bucket {
if let pair = pair {
entrySet.append(pair)
}
}
return entrySet
}
/* */
func keySet() -> [Int] {
var keySet: [Int] = []
for pair in bucket {
if let pair = pair {
keySet.append(pair.key)
}
}
return keySet
}
/* */
func valueSet() -> [String] {
var valueSet: [String] = []
for pair in bucket {
if let pair = pair {
valueSet.append(pair.val)
}
}
return valueSet
}
/* */
func print() {
for entry in entrySet() {
Swift.print("\(entry.key) -> \(entry.val)")
}
}
}
@main
enum _ArrayHashMap {
/* Driver Code */
static func main() {
/* */
let map = ArrayHashMap()
/* */
// (key, value)
map.put(key: 12836, val: "小哈")
map.put(key: 15937, val: "小啰")
map.put(key: 16750, val: "小算")
map.put(key: 13276, val: "小法")
map.put(key: 10583, val: "小鸭")
print("\n添加完成后,哈希表为\nKey -> Value")
map.print()
/* */
// key value
let name = map.get(key: 15937)!
print("\n输入学号 15937 ,查询到姓名 \(name)")
/* */
// (key, value)
map.remove(key: 10583)
print("\n删除 10583 后,哈希表为\nKey -> Value")
map.print()
/* */
print("\n遍历键值对 Key->Value")
for entry in map.entrySet() {
print("\(entry.key) -> \(entry.val)")
}
print("\n单独遍历键 Key")
for key in map.keySet() {
print(key)
}
print("\n单独遍历值 Value")
for val in map.valueSet() {
print(val)
}
}
}

@ -0,0 +1,51 @@
/**
* File: hash_map.swift
* Created Time: 2023-01-16
* Author: nuomi1 (nuomi1@qq.com)
*/
import utils
@main
enum HashMap {
/* Driver Code */
static func main() {
/* */
var map: [Int: String] = [:]
/* */
// (key, value)
map[12836] = "小哈"
map[15937] = "小啰"
map[16750] = "小算"
map[13276] = "小法"
map[10583] = "小鸭"
print("\n添加完成后,哈希表为\nKey -> Value")
PrintUtil.printHashMap(map: map)
/* */
// key value
let name = map[15937]!
print("\n输入学号 15937 ,查询到姓名 \(name)")
/* */
// (key, value)
map.removeValue(forKey: 10583)
print("\n删除 10583 后,哈希表为\nKey -> Value")
PrintUtil.printHashMap(map: map)
/* */
print("\n遍历键值对 Key->Value")
for (key, value) in map {
print("\(key) -> \(value)")
}
print("\n单独遍历键 Key")
for key in map.keys {
print(key)
}
print("\n单独遍历值 Value")
for value in map.values {
print(value)
}
}
}

@ -68,4 +68,10 @@ public enum PrintUtil {
showTrunks(p: p?.prev)
print(p!.str, terminator: "")
}
public static func printHashMap<K, V>(map: [K: V]) {
for (key, value) in map {
print("\(key) -> \(value)")
}
}
}

@ -40,9 +40,9 @@ function search(num: number): TreeNode | null {
// 循环查找,越过叶结点后跳出
while (cur !== null) {
if (cur.val < num) {
cur = cur.right; // 目标结点在 root 的右子树中
cur = cur.right; // 目标结点在 cur 的右子树中
} else if (cur.val > num) {
cur = cur.left; // 目标结点在 root 的左子树中
cur = cur.left; // 目标结点在 cur 的左子树中
} else {
break; // 找到目标结点,跳出循环
}
@ -66,9 +66,9 @@ function insert(num: number): TreeNode | null {
}
pre = cur;
if (cur.val < num) {
cur = cur.right as TreeNode; // 插入位置在 root 的右子树中
cur = cur.right as TreeNode; // 插入位置在 cur 的右子树中
} else {
cur = cur.left as TreeNode; // 插入位置在 root 的左子树中
cur = cur.left as TreeNode; // 插入位置在 cur 的左子树中
}
}
// 插入结点 val

@ -111,6 +111,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
```java title="array.java"
/* 随机返回一个数组元素 */
int randomAccess(int[] nums) {
// 在区间 [0, nums.length) 中随机抽取一个数字
int randomIndex = ThreadLocalRandom.current().
nextInt(0, nums.length);
int randomNum = nums[randomIndex];
@ -136,8 +137,8 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
```python title="array.py"
""" 随机访问元素 """
def randomAccess(nums):
# 在区间 [0, len(nums)) 中随机抽取一个数字
random_index = random.randint(0, len(nums))
# 在区间 [0, len(nums)-1] 中随机抽取一个数字
random_index = random.randint(0, len(nums) - 1)
# 获取并返回随机元素
random_num = nums[random_index]
return random_num
@ -195,7 +196,9 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
int RandomAccess(int[] nums)
{
Random random=new();
// 在区间 [0, nums.Length) 中随机抽取一个数字
int randomIndex = random.Next(nums.Length);
// 获取并返回随机元素
int randomNum = nums[randomIndex];
return randomNum;
}

@ -10,7 +10,7 @@ comments: true
**「逻辑结构」反映了数据之间的逻辑关系**。数组和链表的数据按照顺序依次排列,反映了数据间的线性关系;树从顶至底按层级排列,反映了祖先与后代之间的派生关系;图由结点和边组成,反映了复杂网络关系。
我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线的(例如是网状或树状的),那么就是非线性数据结构。
我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线的(例如是网状或树状的),那么就是非线性数据结构。
- **线性数据结构**:数组、链表、栈、队列、哈希表;
- **非线性数据结构**:树、图、堆、哈希表;

@ -84,13 +84,17 @@ comments: true
=== "JavaScript"
```js title=""
/* JavaScript 的数组可以自由存储各种基本数据类型和对象 */
const array = [0, 0.0, 'a', false];
```
=== "TypeScript"
```typescript title=""
/* 使用多种「基本数据类型」来初始化「数组」 */
const numbers: number[] = [];
const characters: string[] = [];
const booleans: boolean[] = [];
```
=== "C"
@ -126,7 +130,7 @@ comments: true
## 计算机内存
在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度快,但容量较小(通常为 GB 级别)。
在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度快,但容量较小(通常为 GB 级别)。
**算法运行中,相关数据都被存储在内存中**。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。

@ -210,7 +210,24 @@ comments: true
=== "Swift"
```swift title="hash_map.swift"
/* 初始化哈希表 */
var map: [Int: String] = [:]
/* 添加操作 */
// 在哈希表中添加键值对 (key, value)
map[12836] = "小哈"
map[15937] = "小啰"
map[16750] = "小算"
map[13276] = "小法"
map[10583] = "小鸭"
/* 查询操作 */
// 向哈希表输入键 key ,得到值 value
let name = map[15937]!
/* 删除操作 */
// 在哈希表中删除键值对 (key, value)
map.removeValue(forKey: 10583)
```
遍历哈希表有三种方式,即 **遍历键值对、遍历键、遍历值**。
@ -348,7 +365,19 @@ comments: true
=== "Swift"
```swift title="hash_map.swift"
/* 遍历哈希表 */
// 遍历键值对 Key->Value
for (key, value) in map {
print("\(key) -> \(value)")
}
// 单独遍历键 Key
for key in map.keys {
print(key)
}
// 单独遍历值 Value
for value in map.values {
print(value)
}
```
## 哈希函数
@ -771,7 +800,55 @@ $$
=== "Swift"
```swift title="array_hash_map.swift"
/* 键值对 int->String */
class Entry {
var key: Int
var val: String
init(key: Int, val: String) {
self.key = key
self.val = val
}
}
/* 基于数组简易实现的哈希表 */
class ArrayHashMap {
private var bucket: [Entry?] = []
init() {
// 初始化一个长度为 100 的桶(数组)
for _ in 0 ..< 100 {
bucket.append(nil)
}
}
/* 哈希函数 */
private func hashFunc(key: Int) -> Int {
let index = key % 100
return index
}
/* 查询操作 */
func get(key: Int) -> String? {
let index = hashFunc(key: key)
let pair = bucket[index]
return pair?.val
}
/* 添加操作 */
func put(key: Int, val: String) {
let pair = Entry(key: key, val: val)
let index = hashFunc(key: key)
bucket[index] = pair
}
/* 删除操作 */
func remove(key: Int) {
let index = hashFunc(key: key)
// 置为 nil ,代表删除
bucket[index] = nil
}
}
```
## 哈希冲突

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

@ -36,7 +36,7 @@ comments: true
本书主要内容分为复杂度分析、数据结构、算法三个部分。
![mindmap](index.assets/mindmap.png)
![mindmap](about_the_book.assets/mindmap.png)
<p align="center"> Fig. 知识点思维导图 </p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

After

Width:  |  Height:  |  Size: 177 KiB

@ -10,7 +10,7 @@ comments: true
在阅读本书的过程中,若发现某段内容提供了动画或图解,**建议你以图为主线**,将文字内容(一般在图的上方)对齐到图中内容,综合来理解。
![algorithm_animation](suggestions.assets/algorithm_animation.gif)
![animation](suggestions.assets/animation.gif)
## 代码实践学

@ -100,7 +100,7 @@ comments: true
def bubble_sort(nums):
n = len(nums)
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i in range(n - 1, -1, -1):
for i in range(n - 1, 0, -1):
# 内循环:冒泡操作
for j in range(i):
if nums[j] > nums[j + 1]:
@ -288,7 +288,7 @@ comments: true
def bubble_sort_with_flag(nums):
n = len(nums)
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i in range(n - 1, -1, -1):
for i in range(n - 1, 0, -1):
flag = False # 初始化标志位
# 内循环:冒泡操作
for j in range(i):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

@ -43,9 +43,9 @@ comments: true
TreeNode cur = root;
// 循环查找,越过叶结点后跳出
while (cur != null) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > num) cur = cur.left;
// 找到目标结点,跳出循环
else break;
@ -63,9 +63,9 @@ comments: true
TreeNode* cur = root;
// 循环查找,越过叶结点后跳出
while (cur != nullptr) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur->val < num) cur = cur->right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur->val > num) cur = cur->left;
// 找到目标结点,跳出循环
else break;
@ -83,10 +83,10 @@ comments: true
cur = self.root
# 循环查找,越过叶结点后跳出
while cur is not None:
# 目标结点在 root 的右子树中
# 目标结点在 cur 的右子树中
if cur.val < num:
cur = cur.right
# 目标结点在 root 的左子树中
# 目标结点在 cur 的左子树中
elif cur.val > num:
cur = cur.left
# 找到目标结点,跳出循环
@ -104,10 +104,10 @@ comments: true
// 循环查找,越过叶结点后跳出
for node != nil {
if node.Val < num {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
node = node.Right
} else if node.Val > num {
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
node = node.Left
} else {
// 找到目标结点,跳出循环
@ -127,9 +127,9 @@ comments: true
let cur = root;
// 循环查找,越过叶结点后跳出
while (cur !== null) {
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > num) cur = cur.left;
// 找到目标结点,跳出循环
else break;
@ -148,9 +148,9 @@ comments: true
// 循环查找,越过叶结点后跳出
while (cur !== null) {
if (cur.val < num) {
cur = cur.right; // 目标结点在 root 的右子树中
cur = cur.right; // 目标结点在 cur 的右子树中
} else if (cur.val > num) {
cur = cur.left; // 目标结点在 root 的左子树中
cur = cur.left; // 目标结点在 cur 的左子树中
} else {
break; // 找到目标结点,跳出循环
}
@ -176,9 +176,9 @@ comments: true
// 循环查找,越过叶结点后跳出
while (cur != null)
{
// 目标结点在 root 的右子树中
// 目标结点在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 目标结点在 root 的左子树中
// 目标结点在 cur 的左子树中
else if (cur.val > num) cur = cur.left;
// 找到目标结点,跳出循环
else break;
@ -218,9 +218,9 @@ comments: true
// 找到重复结点,直接返回
if (cur.val == num) return null;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur.left;
}
// 插入结点 val
@ -244,9 +244,9 @@ comments: true
// 找到重复结点,直接返回
if (cur->val == num) return nullptr;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur->val < num) cur = cur->right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur->left;
}
// 插入结点 val
@ -276,10 +276,11 @@ comments: true
if cur.val == num:
return None
pre = cur
if cur.val < num: # root
# 插入位置在 cur 的右子树中
if cur.val < num:
cur = cur.right
else: # 插入位置在 root 的左子树中
# 插入位置在 cur 的左子树中
else:
cur = cur.left
# 插入结点 val
@ -339,9 +340,9 @@ comments: true
// 找到重复结点,直接返回
if (cur.val === num) return null;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur.left;
}
// 插入结点 val
@ -370,9 +371,9 @@ comments: true
}
pre = cur;
if (cur.val < num) {
cur = cur.right as TreeNode; // 插入位置在 root 的右子树中
cur = cur.right as TreeNode; // 插入位置在 cur 的右子树中
} else {
cur = cur.left as TreeNode; // 插入位置在 root 的左子树中
cur = cur.left as TreeNode; // 插入位置在 cur 的左子树中
}
}
// 插入结点 val
@ -407,9 +408,9 @@ comments: true
// 找到重复结点,直接返回
if (cur.val == num) return null;
pre = cur;
// 插入位置在 root 的右子树中
// 插入位置在 cur 的右子树中
if (cur.val < num) cur = cur.right;
// 插入位置在 root 的左子树中
// 插入位置在 cur 的左子树中
else cur = cur.left;
}

Loading…
Cancel
Save