Merge branch 'master' into master
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)
|
2
codes/c/chapter_heap/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
add_executable(my_heap my_heap.c)
|
||||
|
185
codes/c/chapter_heap/my_heap.c
Normal file
|
@ -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)
|
||||
|
|
261
codes/c/chapter_tree/avl_tree.c
Normal file
|
@ -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 {
|
||||
// 找到目标结点,跳出循环
|
||||
|
|
67
codes/java/chapter_sorting/radix_sort.java
Normal file
|
@ -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"]),
|
||||
]
|
||||
)
|
||||
|
|
139
codes/swift/chapter_hashing/array_hash_map.swift
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
51
codes/swift/chapter_hashing/hash_map.swift
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 哈希冲突
|
||||
|
|
BIN
docs/chapter_preface/about_the_book.assets/mindmap.png
Normal file
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>
|
||||
|
||||
|
@ -94,7 +94,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -107,7 +107,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -120,7 +120,7 @@ comments: true
|
|||
""" 标题注释,用于标注函数、类、测试样例等 """
|
||||
|
||||
# 内容注释,用于详解代码
|
||||
|
||||
|
||||
"""
|
||||
多行
|
||||
注释
|
||||
|
@ -133,7 +133,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -146,7 +146,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -159,7 +159,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -172,7 +172,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -185,7 +185,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
@ -198,7 +198,7 @@ comments: true
|
|||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
|
|
Before Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 1 MiB |
Before Width: | Height: | Size: 750 KiB |
Before Width: | Height: | Size: 271 KiB |
Before Width: | Height: | Size: 248 KiB |
Before Width: | Height: | Size: 285 KiB |
Before Width: | Height: | Size: 503 KiB |
BIN
docs/chapter_preface/suggestions.assets/animation.gif
Normal file
After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 110 KiB |
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):
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
|
|