mirror of
https://github.com/krahets/hello-algo.git
synced 2025-01-23 22:40:25 +08:00
Merge branch 'master' into heap-dev
This commit is contained in:
commit
d0e5406f0c
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,7 +4,9 @@
|
||||
# Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
hello-algo.iml
|
||||
*.dSYM/
|
||||
|
||||
# mkdocs files
|
||||
site/
|
||||
|
12
codes/c/CMakeLists.txt
Normal file
12
codes/c/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(hello_algo C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
include_directories(./include)
|
||||
|
||||
add_subdirectory(include)
|
||||
add_subdirectory(chapter_computational_complexity)
|
||||
add_subdirectory(chapter_array_and_linkedlist)
|
||||
add_subdirectory(chapter_sorting)
|
||||
add_subdirectory(chapter_tree)
|
1
codes/c/chapter_array_and_linkedlist/CMakeLists.txt
Normal file
1
codes/c/chapter_array_and_linkedlist/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_executable(array array.c)
|
2
codes/c/chapter_computational_complexity/CMakeLists.txt
Normal file
2
codes/c/chapter_computational_complexity/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add_executable(time_complexity time_complexity.c )
|
||||
add_executable(worst_best_time_complexity worst_best_time_complexity.c)
|
@ -56,6 +56,7 @@ int bubbleSort(int *nums, int n) {
|
||||
for (int i = n - 1; i > 0; i--) {
|
||||
// 内循环:冒泡操作
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
int tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
@ -63,6 +64,7 @@ int bubbleSort(int *nums, int n) {
|
||||
count += 3; // 元素交换包含 3 个单元操作
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,5 @@ int main(int argc, char *argv[]) {
|
||||
nums = NULL;
|
||||
}
|
||||
}
|
||||
getchar();
|
||||
return 0;
|
||||
}
|
||||
|
2
codes/c/chapter_sorting/CMakeLists.txt
Normal file
2
codes/c/chapter_sorting/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add_executable(bubble_sort bubble_sort.c)
|
||||
add_executable(insertion_sort insertion_sort.c)
|
@ -7,7 +7,7 @@
|
||||
#include "../include/include.h"
|
||||
|
||||
/* 冒泡排序 */
|
||||
void bubble_sort(int nums[], int size) {
|
||||
void bubbleSort(int nums[], int size) {
|
||||
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for (int i = 0; i < size - 1; i++)
|
||||
{
|
||||
@ -25,7 +25,7 @@ void bubble_sort(int nums[], int size) {
|
||||
}
|
||||
|
||||
/* 冒泡排序(标志优化)*/
|
||||
void bubble_sort_with_flag(int nums[], int size) {
|
||||
void bubbleSortWithFlag(int nums[], int size) {
|
||||
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for (int i = 0; i < size - 1; i++)
|
||||
{
|
||||
@ -50,15 +50,15 @@ void bubble_sort_with_flag(int nums[], int size) {
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[6] = {4, 1, 3, 1, 5, 2};
|
||||
printf("冒泡排序后:\n");
|
||||
bubble_sort(nums, 6);
|
||||
printf("冒泡排序后: ");
|
||||
bubbleSort(nums, 6);
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
printf("%d ", nums[i]);
|
||||
}
|
||||
|
||||
printf("优化版冒泡排序后:\n");
|
||||
bubble_sort_with_flag(nums, 6);
|
||||
printf("\n优化版冒泡排序后: ");
|
||||
bubbleSortWithFlag(nums, 6);
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
printf("%d ", nums[i]);
|
||||
|
@ -28,7 +28,7 @@ void insertionSort(int nums[], int size) {
|
||||
int main() {
|
||||
int nums[] = {4, 1, 3, 1, 5, 2};
|
||||
insertionSort(nums, 6);
|
||||
printf("插入排序完成后 nums = \n");
|
||||
printf("插入排序完成后 nums = ");
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
printf("%d ", nums[i]);
|
||||
|
4
codes/c/chapter_tree/CMakeLists.txt
Normal file
4
codes/c/chapter_tree/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
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)
|
||||
add_executable(binary_search_tree binary_search_tree.c)
|
8
codes/c/chapter_tree/binary_search_tree.c
Normal file
8
codes/c/chapter_tree/binary_search_tree.c
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* File: binary_search_tree.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.h"
|
||||
|
42
codes/c/chapter_tree/binary_tree.c
Normal file
42
codes/c/chapter_tree/binary_tree.c
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* File: binary_tree.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.h"
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化二叉树 */
|
||||
// 初始化结点
|
||||
TreeNode* n1 = newTreeNode(1);
|
||||
TreeNode* n2 = newTreeNode(2);
|
||||
TreeNode* n3 = newTreeNode(3);
|
||||
TreeNode* n4 = newTreeNode(4);
|
||||
TreeNode* n5 = newTreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
n1->left = n2;
|
||||
n1->right = n3;
|
||||
n2->left = n4;
|
||||
n2->right = n5;
|
||||
printf("初始化二叉树\n");
|
||||
printTree(n1);
|
||||
|
||||
/* 插入与删除结点 */
|
||||
TreeNode* P = newTreeNode(0);
|
||||
// 在 n1 -> n2 中间插入结点 P
|
||||
n1->left = P;
|
||||
P->left = n2;
|
||||
printf("插入结点 P 后\n");
|
||||
printTree(n1);
|
||||
|
||||
// 删除结点 P
|
||||
n1->left = n2;
|
||||
// 释放内存
|
||||
free(P);
|
||||
printf("删除结点 P 后\n");
|
||||
printTree(n1);
|
||||
|
||||
return 0;
|
||||
}
|
66
codes/c/chapter_tree/binary_tree_bfs.c
Normal file
66
codes/c/chapter_tree/binary_tree_bfs.c
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* File: binary_tree_bfs.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.h"
|
||||
|
||||
/* 层序遍历 */
|
||||
int *levelOrder(TreeNode *root, int *size) {
|
||||
/* 辅助队列 */
|
||||
int front, rear;
|
||||
int index, *arr;
|
||||
TreeNode *node;
|
||||
TreeNode **queue;
|
||||
|
||||
/* 辅助队列 */
|
||||
queue = (TreeNode **) malloc(sizeof(TreeNode) * MAX_NODE_SIZE);
|
||||
// 队列指针
|
||||
front = 0, rear = 0;
|
||||
// 加入根结点
|
||||
queue[rear++] = root;
|
||||
// 初始化一个列表,用于保存遍历序列
|
||||
/* 辅助数组 */
|
||||
arr = (int *) malloc(sizeof(int) * MAX_NODE_SIZE);
|
||||
// 数组指针
|
||||
index = 0;
|
||||
while (front < rear) {
|
||||
// 队列出队
|
||||
node = queue[front++];
|
||||
// 保存结点
|
||||
arr[index++] = node->val;
|
||||
if (node->left != NULL) {
|
||||
// 左子结点入队
|
||||
queue[rear++] = node->left;
|
||||
}
|
||||
if (node->right != NULL) {
|
||||
// 右子结点入队
|
||||
queue[rear++] = node->right;
|
||||
}
|
||||
}
|
||||
// 更新数组长度的值
|
||||
*size = index;
|
||||
arr = realloc(arr, sizeof(int) * (*size));
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化二叉树 */
|
||||
// 这里借助了一个从数组直接生成二叉树的函数
|
||||
int nums[] = {1, 2, 3, NIL, 5, 6, NIL};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
TreeNode *root = arrToTree(nums, size);
|
||||
printf("初始化二叉树\n");
|
||||
printTree(root);
|
||||
|
||||
/* 层序遍历 */
|
||||
// 需要传入数组的长度
|
||||
int *arr = levelOrder(root, &size);
|
||||
printf("层序遍历的结点打印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
return 0;
|
||||
}
|
72
codes/c/chapter_tree/binary_tree_dfs.c
Normal file
72
codes/c/chapter_tree/binary_tree_dfs.c
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* File: binary_tree_dfs.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.h"
|
||||
|
||||
/* 辅助数组,用于存储遍历序列 */
|
||||
int *arr;
|
||||
|
||||
/* 前序遍历 */
|
||||
void preOrder(TreeNode *root, int *size) {
|
||||
|
||||
if (root == NULL) return;
|
||||
// 访问优先级:根结点 -> 左子树 -> 右子树
|
||||
arr[(*size)++] = root->val;
|
||||
preOrder(root->left, size);
|
||||
preOrder(root->right, size);
|
||||
}
|
||||
|
||||
/* 中序遍历 */
|
||||
void inOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL) return;
|
||||
// 访问优先级:左子树 -> 根结点 -> 右子树
|
||||
inOrder(root->left, size);
|
||||
arr[(*size)++] = root->val;
|
||||
inOrder(root->right, size);
|
||||
}
|
||||
|
||||
/* 后序遍历 */
|
||||
void postOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL) return;
|
||||
// 访问优先级:左子树 -> 右子树 -> 根结点
|
||||
postOrder(root->left, size);
|
||||
postOrder(root->right, size);
|
||||
arr[(*size)++] = root->val;
|
||||
}
|
||||
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化二叉树 */
|
||||
// 这里借助了一个从数组直接生成二叉树的函数
|
||||
int nums[] = {1, 2, 3, 4, 5, 6, 7};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
TreeNode *root = arrToTree(nums, size);
|
||||
printf("初始化二叉树\n");
|
||||
printTree(root);
|
||||
|
||||
/* 前序遍历 */
|
||||
// 初始化辅助数组
|
||||
arr = (int *) malloc(sizeof(int) * MAX_NODE_SIZE);
|
||||
size = 0;
|
||||
preOrder(root, &size);
|
||||
printf("前序遍历的结点打印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
/* 中序遍历 */
|
||||
size = 0;
|
||||
inOrder(root, &size);
|
||||
printf("中序遍历的结点打印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
/* 后序遍历 */
|
||||
size = 0;
|
||||
postOrder(root, &size);
|
||||
printf("后序遍历的结点打印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
return 0;
|
||||
}
|
4
codes/c/include/CMakeLists.txt
Normal file
4
codes/c/include/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
add_executable(include
|
||||
include_test.c
|
||||
include.h print_util.h
|
||||
list_node.h tree_node.h)
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* File: PrintUtil.h
|
||||
* Created Time: 2022-12-21
|
||||
* Author: MolDum (moldum@163.com)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// #include "ListNode.h"
|
||||
// #include "TreeNode.h"
|
||||
|
||||
/**
|
||||
* @brief Print an Array
|
||||
*
|
||||
* @param arr
|
||||
* @param n
|
||||
*/
|
||||
|
||||
static void printArray(int* arr, int n)
|
||||
{
|
||||
printf("[");
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
printf("%d, ", arr[i]);
|
||||
}
|
||||
printf("%d]\n", arr[n-1]);
|
||||
}
|
@ -1,13 +1,28 @@
|
||||
/**
|
||||
* File: include.h
|
||||
* Created Time: 2022-12-20
|
||||
* Author: MolDuM (moldum@163.com)
|
||||
* Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#ifndef C_INCLUDE_H
|
||||
#define C_INCLUDE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "PrintUtil.h"
|
||||
#include "list_node.h"
|
||||
#include "tree_node.h"
|
||||
#include "print_util.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // C_INCLUDE_H
|
||||
|
38
codes/c/include/include_test.c
Normal file
38
codes/c/include/include_test.c
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* File: include_test.c
|
||||
* Created Time: 2023-01-10
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "include.h"
|
||||
|
||||
void testListNode() {
|
||||
int nums[] = {2, 3, 5, 6, 7};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
ListNode *head = arrToLinkedList(nums, size);
|
||||
printLinkedList(head);
|
||||
|
||||
ListNode *node = getListNode(head, 5);
|
||||
printf("find node: %d\n", node->val);
|
||||
}
|
||||
|
||||
void testTreeNode() {
|
||||
int nums[] = {1, 2, 3, NIL, 5, 6, NIL};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
TreeNode *root = arrToTree(nums, size);
|
||||
|
||||
// print tree
|
||||
printTree(root);
|
||||
|
||||
// tree to arr
|
||||
int *arr = treeToArr(root);
|
||||
printArray(arr, size);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("==testListNode==\n");
|
||||
testListNode();
|
||||
printf("==testTreeNode==\n");
|
||||
testTreeNode();
|
||||
return 0;
|
||||
}
|
72
codes/c/include/list_node.h
Normal file
72
codes/c/include/list_node.h
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* File: list_node.h
|
||||
* Created Time: 2023-01-09
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
#ifndef LIST_NODE_H
|
||||
#define LIST_NODE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Definition for a singly-linked list node
|
||||
*
|
||||
*/
|
||||
struct ListNode {
|
||||
int val; // 结点值
|
||||
struct ListNode *next; // 指向下一结点的指针(引用)
|
||||
};
|
||||
|
||||
// typedef 为 C 语言的关键字,作用是为一种数据类型定义一个新名字
|
||||
typedef struct ListNode ListNode;
|
||||
|
||||
ListNode *newListNode(int val) {
|
||||
ListNode *node, *next;
|
||||
node = (ListNode *) malloc(sizeof(ListNode));
|
||||
node->val = val;
|
||||
node->next = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a linked list with a vector
|
||||
*
|
||||
* @param list
|
||||
* @return ListNode*
|
||||
*/
|
||||
|
||||
ListNode *arrToLinkedList(const int *arr, size_t size) {
|
||||
if (size <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode *dummy = newListNode(0);
|
||||
ListNode *node = dummy;
|
||||
for (int i = 0; i < size; i++) {
|
||||
node->next = newListNode(arr[i]);
|
||||
node = node->next;
|
||||
}
|
||||
return dummy->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a list node with specific value from a linked list
|
||||
*
|
||||
* @param head
|
||||
* @param val
|
||||
* @return ListNode*
|
||||
*/
|
||||
ListNode *getListNode(ListNode *head, int val) {
|
||||
while (head != NULL && head->val != val) {
|
||||
head = head->next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIST_NODE_H
|
135
codes/c/include/print_util.h
Normal file
135
codes/c/include/print_util.h
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* File: print_util.h
|
||||
* Created Time: 2022-12-21
|
||||
* Author: MolDum (moldum@163.com)、Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#ifndef PRINT_UTIL_H
|
||||
#define PRINT_UTIL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "list_node.h"
|
||||
#include "tree_node.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Print an Array
|
||||
*
|
||||
* @param arr
|
||||
* @param size
|
||||
*/
|
||||
static void printArray(int *arr, int size) {
|
||||
printf("[");
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
if (arr[i] != NIL) {
|
||||
printf("%d, ", arr[i]);
|
||||
} else {
|
||||
printf("NULL, ");
|
||||
}
|
||||
}
|
||||
if (arr[size - 1] != NIL) {
|
||||
printf("%d]\n", arr[size - 1]);
|
||||
}else{
|
||||
printf("NULL]\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print a linked list
|
||||
*
|
||||
* @param head
|
||||
*/
|
||||
static void printLinkedList(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
while (node->next != NULL) {
|
||||
printf("%d -> ", node->val);
|
||||
node = node->next;
|
||||
}
|
||||
printf("%d\n", node->val);
|
||||
}
|
||||
|
||||
struct Trunk {
|
||||
struct Trunk *prev;
|
||||
char *str;
|
||||
};
|
||||
|
||||
typedef struct Trunk Trunk;
|
||||
|
||||
Trunk *newTrunk(Trunk *prev, char *str) {
|
||||
Trunk *trunk = (Trunk *) malloc(sizeof(Trunk));
|
||||
trunk->prev = prev;
|
||||
trunk->str = (char *) malloc(sizeof(char) * 10);
|
||||
strcpy(trunk->str, str);
|
||||
return trunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to print branches of the binary tree
|
||||
*
|
||||
* @param trunk
|
||||
*/
|
||||
void showTrunks(Trunk *trunk) {
|
||||
if (trunk == NULL) {
|
||||
return;
|
||||
}
|
||||
showTrunks(trunk->prev);
|
||||
printf("%s", trunk->str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Help to print a binary tree, hide more details
|
||||
* @param node
|
||||
* @param prev
|
||||
* @param isLeft
|
||||
*/
|
||||
static void printTreeHelper(TreeNode *node, Trunk *prev, bool isLeft) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
char *prev_str = " ";
|
||||
Trunk *trunk = newTrunk(prev, prev_str);
|
||||
printTreeHelper(node->right, trunk, true);
|
||||
if (prev == NULL) {
|
||||
trunk->str = "———";
|
||||
} else if (isLeft) {
|
||||
trunk->str = "/———";
|
||||
prev_str = " |";
|
||||
} else {
|
||||
trunk->str = "\\———";
|
||||
prev->str = prev_str;
|
||||
}
|
||||
showTrunks(trunk);
|
||||
printf("%d\n", node->val);
|
||||
|
||||
if (prev != NULL) {
|
||||
prev->str = prev_str;
|
||||
}
|
||||
trunk->str = " |";
|
||||
|
||||
printTreeHelper(node->left, trunk, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print a binary tree
|
||||
*
|
||||
* @param head
|
||||
*/
|
||||
static void printTree(TreeNode *root) {
|
||||
printTreeHelper(root, NULL, false);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PRINT_UTIL_H
|
131
codes/c/include/tree_node.h
Normal file
131
codes/c/include/tree_node.h
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* File: tree_node.h
|
||||
* Created Time: 2023-01-09
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TREE_NODE_H
|
||||
#define TREE_NODE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NIL ('#')
|
||||
#define MAX_NODE_SIZE 5000
|
||||
|
||||
struct TreeNode {
|
||||
int val;
|
||||
int height;
|
||||
struct TreeNode *left;
|
||||
struct TreeNode *right;
|
||||
};
|
||||
|
||||
typedef struct TreeNode TreeNode;
|
||||
|
||||
|
||||
TreeNode *newTreeNode(int val) {
|
||||
TreeNode *node;
|
||||
|
||||
node = (TreeNode *) malloc(sizeof(TreeNode));
|
||||
node->val = val;
|
||||
node->height = 0;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a binary tree with an array
|
||||
*
|
||||
* @param arr
|
||||
* @param size
|
||||
* @return TreeNode *
|
||||
*/
|
||||
TreeNode *arrToTree(const int *arr, size_t size) {
|
||||
if (size <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int front, rear, index;
|
||||
TreeNode *root, *node;
|
||||
TreeNode **queue;
|
||||
|
||||
/* 根结点 */
|
||||
root = newTreeNode(arr[0]);
|
||||
/* 辅助队列 */
|
||||
queue = (TreeNode **) malloc(sizeof(TreeNode) * MAX_NODE_SIZE);
|
||||
// 队列指针
|
||||
front = 0, rear = 0;
|
||||
// 将根结点放入队尾
|
||||
queue[rear++] = root;
|
||||
// 记录遍历数组的索引
|
||||
index = 0;
|
||||
while (front < rear) {
|
||||
// 取队列中的头结点,并让头结点出队
|
||||
node = queue[front++];
|
||||
index++;
|
||||
if (index < size) {
|
||||
if (arr[index] != NIL) {
|
||||
node->left = newTreeNode(arr[index]);
|
||||
queue[rear++] = node->left;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
if (index < size) {
|
||||
if (arr[index] != NIL) {
|
||||
node->right = newTreeNode(arr[index]);
|
||||
queue[rear++] = node->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Generate a binary tree with an array
|
||||
*
|
||||
* @param arr
|
||||
* @param size
|
||||
* @return TreeNode *
|
||||
*/
|
||||
int *treeToArr(TreeNode *root) {
|
||||
if (root == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int front, rear;
|
||||
int index, *arr;
|
||||
TreeNode *node;
|
||||
TreeNode **queue;
|
||||
/* 辅助队列 */
|
||||
queue = (TreeNode **) malloc(sizeof(TreeNode) * MAX_NODE_SIZE);
|
||||
// 队列指针
|
||||
front = 0, rear = 0;
|
||||
// 将根结点放入队尾
|
||||
queue[rear++] = root;
|
||||
/* 辅助数组 */
|
||||
arr = (int *) malloc(sizeof(int) * MAX_NODE_SIZE);
|
||||
// 数组指针
|
||||
index = 0;
|
||||
while (front < rear) {
|
||||
// 取队列中的头结点,并让头结点出队
|
||||
node = queue[front++];
|
||||
if (node != NULL) {
|
||||
arr[index] = node->val;
|
||||
queue[rear++] = node->left;
|
||||
queue[rear++] = node->right;
|
||||
} else {
|
||||
arr[index] = NIL;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_NODE_H
|
@ -28,9 +28,9 @@ void remove(ListNode* n0) {
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
ListNode* access(ListNode* head, int index) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
head = head->next;
|
||||
if (head == nullptr)
|
||||
return nullptr;
|
||||
head = head->next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
@ -1,228 +0,0 @@
|
||||
/**
|
||||
* File: avl_tree.cpp
|
||||
* Created Time: 2022-12-2
|
||||
* Author: mgisr (maguagua0706@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../include/include.hpp"
|
||||
|
||||
class AvlTree {
|
||||
private:
|
||||
TreeNode *root{};
|
||||
static bool isBalance(const TreeNode *p);
|
||||
static int getBalanceFactor(const TreeNode *p);
|
||||
static void updateHeight(TreeNode *p);
|
||||
void fixBalance(TreeNode *p);
|
||||
static bool isLeftChild(const TreeNode *p);
|
||||
static TreeNode *&fromParentTo(TreeNode *node);
|
||||
public:
|
||||
AvlTree() = default;
|
||||
AvlTree(const AvlTree &p) = default;
|
||||
const TreeNode *search(int val);
|
||||
bool insert(int val);
|
||||
bool remove(int val);
|
||||
void printTree();
|
||||
};
|
||||
|
||||
// 判断该结点是否平衡
|
||||
bool AvlTree::isBalance(const TreeNode *p) {
|
||||
int balance_factor = getBalanceFactor(p);
|
||||
if (-1 <= balance_factor && balance_factor <= 1) { return true; }
|
||||
else { return false; }
|
||||
}
|
||||
|
||||
// 获取当前结点的平衡因子
|
||||
int AvlTree::getBalanceFactor(const TreeNode *p) {
|
||||
if (p->left == nullptr && p->right == nullptr) { return 0; }
|
||||
else if (p->left == nullptr) { return (-1 - p->right->height); }
|
||||
else if (p->right == nullptr) { return p->left->height + 1; }
|
||||
else { return p->left->height - p->right->height; }
|
||||
}
|
||||
|
||||
// 更新结点高度
|
||||
void AvlTree::updateHeight(TreeNode *p) {
|
||||
if (p->left == nullptr && p->right == nullptr) { p->height = 0; }
|
||||
else if (p->left == nullptr) { p->height = p->right->height + 1; }
|
||||
else if (p->right == nullptr) { p->height = p->left->height + 1; }
|
||||
else { p->height = std::max(p->left->height, p->right->height) + 1; }
|
||||
}
|
||||
|
||||
void AvlTree::fixBalance(TreeNode *p) {
|
||||
// 左旋操作
|
||||
auto rotate_left = [&](TreeNode *node) -> TreeNode * {
|
||||
TreeNode *temp = node->right;
|
||||
temp->parent = p->parent;
|
||||
node->right = temp->left;
|
||||
if (temp->left != nullptr) {
|
||||
temp->left->parent = node;
|
||||
}
|
||||
temp->left = node;
|
||||
node->parent = temp;
|
||||
updateHeight(node);
|
||||
updateHeight(temp);
|
||||
return temp;
|
||||
};
|
||||
// 右旋操作
|
||||
auto rotate_right = [&](TreeNode *node) -> TreeNode * {
|
||||
TreeNode *temp = node->left;
|
||||
temp->parent = p->parent;
|
||||
node->left = temp->right;
|
||||
if (temp->right != nullptr) {
|
||||
temp->right->parent = node;
|
||||
}
|
||||
temp->right = node;
|
||||
node->parent = temp;
|
||||
updateHeight(node);
|
||||
updateHeight(temp);
|
||||
return temp;
|
||||
};
|
||||
// 根据规则选取旋转方式
|
||||
if (getBalanceFactor(p) > 1) {
|
||||
if (getBalanceFactor(p->left) > 0) {
|
||||
if (p->parent == nullptr) { root = rotate_right(p); }
|
||||
else { fromParentTo(p) = rotate_right(p); }
|
||||
} else {
|
||||
p->left = rotate_left(p->left);
|
||||
if (p->parent == nullptr) { root = rotate_right(p); }
|
||||
else { fromParentTo(p) = rotate_right(p); }
|
||||
}
|
||||
} else {
|
||||
if (getBalanceFactor(p->right) < 0) {
|
||||
if (p->parent == nullptr) { root = rotate_left(p); }
|
||||
else { fromParentTo(p) = rotate_left(p); }
|
||||
} else {
|
||||
p->right = rotate_right(p->right);
|
||||
if (p->parent == nullptr) { root = rotate_left(p); }
|
||||
else { fromParentTo(p) = rotate_left(p); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断当前结点是否为其父节点的左孩子
|
||||
bool AvlTree::isLeftChild(const TreeNode *p) {
|
||||
if (p->parent == nullptr) { return false; }
|
||||
return (p->parent->left == p);
|
||||
}
|
||||
|
||||
// 返回父节点指向当前结点指针的引用
|
||||
TreeNode *&AvlTree::fromParentTo(TreeNode *node) {
|
||||
if (isLeftChild(node)) { return node->parent->left; }
|
||||
else { return node->parent->right; }
|
||||
}
|
||||
|
||||
const TreeNode *AvlTree::search(int val) {
|
||||
TreeNode *p = root;
|
||||
while (p != nullptr) {
|
||||
if (p->val == val) { return p; }
|
||||
else if (p->val > val) { p = p->left; }
|
||||
else { p = p->right; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AvlTree::insert(int val) {
|
||||
TreeNode *p = root;
|
||||
if (p == nullptr) {
|
||||
root = new TreeNode(val);
|
||||
return true;
|
||||
}
|
||||
for (;;) {
|
||||
if (p->val == val) { return false; }
|
||||
else if (p->val > val) {
|
||||
if (p->left == nullptr) {
|
||||
p->left = new TreeNode(val, p);
|
||||
break;
|
||||
} else {
|
||||
p = p->left;
|
||||
}
|
||||
} else {
|
||||
if (p->right == nullptr) {
|
||||
p->right = new TreeNode(val, p);
|
||||
break;
|
||||
} else {
|
||||
p = p->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (; p != nullptr; p = p->parent) {
|
||||
if (!isBalance(p)) {
|
||||
fixBalance(p);
|
||||
break;
|
||||
} else { updateHeight(p); }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AvlTree::remove(int val) {
|
||||
TreeNode *p = root;
|
||||
if (p == nullptr) { return false; }
|
||||
while (p != nullptr) {
|
||||
if (p->val == val) {
|
||||
TreeNode *real_delete_node = p;
|
||||
TreeNode *next_node;
|
||||
if (p->left == nullptr) {
|
||||
next_node = p->right;
|
||||
if (p->parent == nullptr) { root = next_node; }
|
||||
else { fromParentTo(p) = next_node; }
|
||||
} else if (p->right == nullptr) {
|
||||
next_node = p->left;
|
||||
if (p->parent == nullptr) { root = next_node; }
|
||||
else { fromParentTo(p) = next_node; }
|
||||
} else {
|
||||
while (real_delete_node->left != nullptr) {
|
||||
real_delete_node = real_delete_node->left;
|
||||
}
|
||||
std::swap(p->val, real_delete_node->val);
|
||||
next_node = real_delete_node->right;
|
||||
if (real_delete_node->parent == p) { p->right = next_node; }
|
||||
else { real_delete_node->parent->left = next_node; }
|
||||
}
|
||||
if (next_node != nullptr) {
|
||||
next_node->parent = real_delete_node->parent;
|
||||
}
|
||||
for (p = real_delete_node; p != nullptr; p = p->parent) {
|
||||
if (!isBalance(p)) { fixBalance(p); }
|
||||
updateHeight(p);
|
||||
}
|
||||
delete real_delete_node;
|
||||
return true;
|
||||
} else if (p->val > val) {
|
||||
p = p->left;
|
||||
} else {
|
||||
p = p->right;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void inOrder(const TreeNode *root) {
|
||||
if (root == nullptr) return;
|
||||
inOrder(root->left);
|
||||
cout << root->val << ' ';
|
||||
inOrder(root->right);
|
||||
}
|
||||
|
||||
void AvlTree::printTree() {
|
||||
inOrder(root);
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
AvlTree tree = AvlTree();
|
||||
// tree.insert(13);
|
||||
// tree.insert(24);
|
||||
// tree.insert(37);
|
||||
// tree.insert(90);
|
||||
// tree.insert(53);
|
||||
|
||||
tree.insert(53);
|
||||
tree.insert(90);
|
||||
tree.insert(37);
|
||||
tree.insert(24);
|
||||
tree.insert(13);
|
||||
tree.remove(90);
|
||||
tree.printTree();
|
||||
const TreeNode *p = tree.search(37);
|
||||
cout << p->val;
|
||||
return 0;
|
||||
}
|
@ -133,7 +133,7 @@ int main() {
|
||||
PrintUtil::printTree(bst->getRoot());
|
||||
|
||||
/* 查找结点 */
|
||||
TreeNode* node = bst->search(5);
|
||||
TreeNode* node = bst->search(7);
|
||||
cout << endl << "查找到的结点对象为 " << node << ",结点值 = " << node->val << endl;
|
||||
|
||||
/* 插入结点 */
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
|
||||
#include "ListNode.hpp"
|
||||
#include "TreeNode.hpp"
|
||||
|
@ -8,9 +8,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
{
|
||||
public class Array
|
||||
{
|
||||
/// <summary>
|
||||
/// 随机返回一个数组元素
|
||||
/// </summary>
|
||||
/* 随机返回一个数组元素 */
|
||||
public static int RandomAccess(int[] nums)
|
||||
{
|
||||
Random random = new();
|
||||
@ -19,9 +17,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
return randomNum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扩展数组长度
|
||||
/// </summary>
|
||||
/* 扩展数组长度 */
|
||||
public static int[] Extend(int[] nums, int enlarge)
|
||||
{
|
||||
// 初始化一个扩展长度后的数组
|
||||
@ -35,9 +31,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在数组的索引 index 处插入元素 num
|
||||
/// </summary>
|
||||
/* 在数组的索引 index 处插入元素 num */
|
||||
public static void Insert(int[] nums, int num, int index)
|
||||
{
|
||||
// 把索引 index 以及之后的所有元素向后移动一位
|
||||
@ -49,9 +43,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
nums[index] = num;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除索引 index 处元素
|
||||
/// </summary>
|
||||
/* 删除索引 index 处元素 */
|
||||
public static void Remove(int[] nums, int index)
|
||||
{
|
||||
// 把索引 index 之后的所有元素向前移动一位
|
||||
@ -61,9 +53,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 遍历数组
|
||||
/// </summary>
|
||||
/* 遍历数组 */
|
||||
public static void Traverse(int[] nums)
|
||||
{
|
||||
int count = 0;
|
||||
@ -79,9 +69,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在数组中查找指定元素
|
||||
/// </summary>
|
||||
/* 在数组中查找指定元素 */
|
||||
public static int Find(int[] nums, int target)
|
||||
{
|
||||
for (int i = 0; i < nums.Length; i++)
|
||||
@ -92,15 +80,13 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助函数,数组转字符串
|
||||
/// </summary>
|
||||
/* 辅助函数,数组转字符串 */
|
||||
public static string ToString(int[] nums)
|
||||
{
|
||||
return string.Join(",", nums);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
|
||||
[Test]
|
||||
public static void Test()
|
||||
{
|
||||
|
@ -9,9 +9,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
{
|
||||
public class linked_list
|
||||
{
|
||||
/// <summary>
|
||||
/// 在链表的结点 n0 之后插入结点 P
|
||||
/// </summary>
|
||||
/* 在链表的结点 n0 之后插入结点 P */
|
||||
public static void Insert(ListNode n0, ListNode P)
|
||||
{
|
||||
ListNode? n1 = n0.next;
|
||||
@ -19,9 +17,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
P.next = n1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除链表的结点 n0 之后的首个结点
|
||||
/// </summary>
|
||||
/* 删除链表的结点 n0 之后的首个结点 */
|
||||
public static void Remove(ListNode n0)
|
||||
{
|
||||
if (n0.next == null)
|
||||
@ -32,23 +28,19 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
n0.next = n1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 访问链表中索引为 index 的结点
|
||||
/// </summary>
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
public static ListNode? Access(ListNode head, int index)
|
||||
{
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
head = head.next;
|
||||
if (head == null)
|
||||
return null;
|
||||
head = head.next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在链表中查找值为 target 的首个结点
|
||||
/// </summary>
|
||||
/* 在链表中查找值为 target 的首个结点 */
|
||||
public static int Find(ListNode head, int target)
|
||||
{
|
||||
int index = 0;
|
||||
@ -62,7 +54,7 @@ namespace hello_algo.chapter_array_and_linkedlist
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
|
@ -35,11 +35,7 @@ namespace hello_algo.chapter_tree
|
||||
return root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找结点
|
||||
/// </summary>
|
||||
/// <param name="num"></param>
|
||||
/// <returns></returns>
|
||||
/* 查找结点 */
|
||||
public TreeNode? search(int num)
|
||||
{
|
||||
TreeNode? cur = root;
|
||||
@ -163,7 +159,7 @@ namespace hello_algo.chapter_tree
|
||||
PrintUtil.PrintTree(bst.getRoot());
|
||||
|
||||
/* 查找结点 */
|
||||
TreeNode? node = bst.search(5);
|
||||
TreeNode? node = bst.search(7);
|
||||
Console.WriteLine("\n查找到的结点对象为 " + node + ",结点值 = " + node.val);
|
||||
|
||||
/* 插入结点 */
|
||||
|
@ -12,11 +12,7 @@ namespace hello_algo.chapter_tree
|
||||
public class binary_tree_bfs
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 层序遍历
|
||||
/// </summary>
|
||||
/// <param name="root"></param>
|
||||
/// <returns></returns>
|
||||
/* 层序遍历 */
|
||||
public List<int> hierOrder(TreeNode root)
|
||||
{
|
||||
// 初始化队列,加入根结点
|
||||
|
@ -13,10 +13,7 @@ namespace hello_algo.chapter_tree
|
||||
{
|
||||
List<int> list = new();
|
||||
|
||||
/// <summary>
|
||||
/// 前序遍历
|
||||
/// </summary>
|
||||
/// <param name="root"></param>
|
||||
/* 前序遍历 */
|
||||
void preOrder(TreeNode? root)
|
||||
{
|
||||
if (root == null) return;
|
||||
@ -26,10 +23,7 @@ namespace hello_algo.chapter_tree
|
||||
preOrder(root.right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中序遍历
|
||||
/// </summary>
|
||||
/// <param name="root"></param>
|
||||
/* 中序遍历 */
|
||||
void inOrder(TreeNode? root)
|
||||
{
|
||||
if (root == null) return;
|
||||
@ -39,10 +33,7 @@ namespace hello_algo.chapter_tree
|
||||
inOrder(root.right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后序遍历
|
||||
/// </summary>
|
||||
/// <param name="root"></param>
|
||||
/* 后序遍历 */
|
||||
void postOrder(TreeNode? root)
|
||||
{
|
||||
if (root == null) return;
|
||||
|
@ -29,10 +29,10 @@ func removeNode(n0 *ListNode) {
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
func access(head *ListNode, index int) *ListNode {
|
||||
for i := 0; i < index; i++ {
|
||||
head = head.Next
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
head = head.Next
|
||||
}
|
||||
return head
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
package chapter_array_and_linkedlist
|
||||
|
||||
/* 列表类简易实现 */
|
||||
type MyList struct {
|
||||
type myList struct {
|
||||
numsCapacity int
|
||||
nums []int
|
||||
numsSize int
|
||||
@ -13,8 +13,8 @@ type MyList struct {
|
||||
}
|
||||
|
||||
/* 构造函数 */
|
||||
func newMyList() *MyList {
|
||||
return &MyList{
|
||||
func newMyList() *myList {
|
||||
return &myList{
|
||||
numsCapacity: 10, // 列表容量
|
||||
nums: make([]int, 10), // 数组(存储列表元素)
|
||||
numsSize: 0, // 列表长度(即当前元素数量)
|
||||
@ -23,17 +23,17 @@ func newMyList() *MyList {
|
||||
}
|
||||
|
||||
/* 获取列表长度(即当前元素数量) */
|
||||
func (l *MyList) size() int {
|
||||
func (l *myList) size() int {
|
||||
return l.numsSize
|
||||
}
|
||||
|
||||
/* 获取列表容量 */
|
||||
func (l *MyList) capacity() int {
|
||||
func (l *myList) capacity() int {
|
||||
return l.numsCapacity
|
||||
}
|
||||
|
||||
/* 访问元素 */
|
||||
func (l *MyList) get(index int) int {
|
||||
func (l *myList) get(index int) int {
|
||||
// 索引如果越界则抛出异常,下同
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
@ -42,7 +42,7 @@ func (l *MyList) get(index int) int {
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
func (l *MyList) set(num, index int) {
|
||||
func (l *myList) set(num, index int) {
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
}
|
||||
@ -50,7 +50,7 @@ func (l *MyList) set(num, index int) {
|
||||
}
|
||||
|
||||
/* 尾部添加元素 */
|
||||
func (l *MyList) add(num int) {
|
||||
func (l *myList) add(num int) {
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if l.numsSize == l.numsCapacity {
|
||||
l.extendCapacity()
|
||||
@ -61,7 +61,7 @@ func (l *MyList) add(num int) {
|
||||
}
|
||||
|
||||
/* 中间插入元素 */
|
||||
func (l *MyList) insert(num, index int) {
|
||||
func (l *myList) insert(num, index int) {
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
}
|
||||
@ -79,7 +79,7 @@ func (l *MyList) insert(num, index int) {
|
||||
}
|
||||
|
||||
/* 删除元素 */
|
||||
func (l *MyList) remove(index int) int {
|
||||
func (l *myList) remove(index int) int {
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
}
|
||||
@ -95,7 +95,7 @@ func (l *MyList) remove(index int) int {
|
||||
}
|
||||
|
||||
/* 列表扩容 */
|
||||
func (l *MyList) extendCapacity() {
|
||||
func (l *myList) extendCapacity() {
|
||||
// 新建一个长度为 self.__size 的数组,并将原数组拷贝到新数组
|
||||
l.nums = append(l.nums, make([]int, l.numsCapacity*(l.extendRatio-1))...)
|
||||
// 更新列表容量
|
||||
@ -103,7 +103,7 @@ func (l *MyList) extendCapacity() {
|
||||
}
|
||||
|
||||
/* 返回有效长度的列表 */
|
||||
func (l *MyList) toArray() []int {
|
||||
func (l *myList) toArray() []int {
|
||||
// 仅转换有效长度范围内的列表元素
|
||||
return l.nums[:l.numsSize]
|
||||
}
|
||||
|
@ -9,31 +9,31 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
/* Node 结构体 */
|
||||
type Node struct {
|
||||
/* 结构体 */
|
||||
type node struct {
|
||||
val int
|
||||
next *Node
|
||||
next *node
|
||||
}
|
||||
|
||||
/* TreeNode 二叉树 */
|
||||
type TreeNode struct {
|
||||
/* treeNode 二叉树 */
|
||||
type treeNode struct {
|
||||
val int
|
||||
left *TreeNode
|
||||
right *TreeNode
|
||||
left *treeNode
|
||||
right *treeNode
|
||||
}
|
||||
|
||||
/* 创建 Node 结构体 */
|
||||
func newNode(val int) *Node {
|
||||
return &Node{val: val}
|
||||
/* 创建 node 结构体 */
|
||||
func newNode(val int) *node {
|
||||
return &node{val: val}
|
||||
}
|
||||
|
||||
/* 创建 TreeNode 结构体 */
|
||||
func newTreeNode(val int) *TreeNode {
|
||||
return &TreeNode{val: val}
|
||||
/* 创建 treeNode 结构体 */
|
||||
func newTreeNode(val int) *treeNode {
|
||||
return &treeNode{val: val}
|
||||
}
|
||||
|
||||
/* 输出二叉树 */
|
||||
func printTree(root *TreeNode) {
|
||||
func printTree(root *treeNode) {
|
||||
if root == nil {
|
||||
return
|
||||
}
|
||||
@ -72,7 +72,7 @@ func spaceLinear(n int) {
|
||||
// 长度为 n 的数组占用 O(n) 空间
|
||||
_ = make([]int, n)
|
||||
// 长度为 n 的列表占用 O(n) 空间
|
||||
var nodes []*Node
|
||||
var nodes []*node
|
||||
for i := 0; i < n; i++ {
|
||||
nodes = append(nodes, newNode(i))
|
||||
}
|
||||
@ -112,7 +112,7 @@ func spaceQuadraticRecur(n int) int {
|
||||
}
|
||||
|
||||
/* 指数阶(建立满二叉树) */
|
||||
func buildTree(n int) *TreeNode {
|
||||
func buildTree(n int) *treeNode {
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -7,30 +7,30 @@ package chapter_hashing
|
||||
import "fmt"
|
||||
|
||||
/* 键值对 int->String */
|
||||
type Entry struct {
|
||||
type entry struct {
|
||||
key int
|
||||
val string
|
||||
}
|
||||
|
||||
/* 基于数组简易实现的哈希表 */
|
||||
type ArrayHashMap struct {
|
||||
bucket []*Entry
|
||||
type arrayHashMap struct {
|
||||
bucket []*entry
|
||||
}
|
||||
|
||||
func newArrayHashMap() *ArrayHashMap {
|
||||
func newArrayHashMap() *arrayHashMap {
|
||||
// 初始化一个长度为 100 的桶(数组)
|
||||
bucket := make([]*Entry, 100)
|
||||
return &ArrayHashMap{bucket: bucket}
|
||||
bucket := make([]*entry, 100)
|
||||
return &arrayHashMap{bucket: bucket}
|
||||
}
|
||||
|
||||
/* 哈希函数 */
|
||||
func (a *ArrayHashMap) hashFunc(key int) int {
|
||||
func (a *arrayHashMap) hashFunc(key int) int {
|
||||
index := key % 100
|
||||
return index
|
||||
}
|
||||
|
||||
/* 查询操作 */
|
||||
func (a *ArrayHashMap) get(key int) string {
|
||||
func (a *arrayHashMap) get(key int) string {
|
||||
index := a.hashFunc(key)
|
||||
pair := a.bucket[index]
|
||||
if pair == nil {
|
||||
@ -40,22 +40,22 @@ func (a *ArrayHashMap) get(key int) string {
|
||||
}
|
||||
|
||||
/* 添加操作 */
|
||||
func (a *ArrayHashMap) put(key int, val string) {
|
||||
pair := &Entry{key: key, val: 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) {
|
||||
func (a *arrayHashMap) remove(key int) {
|
||||
index := a.hashFunc(key)
|
||||
// 置为 nil ,代表删除
|
||||
a.bucket[index] = nil
|
||||
}
|
||||
|
||||
/* 获取所有键对 */
|
||||
func (a *ArrayHashMap) entrySet() []*Entry {
|
||||
var pairs []*Entry
|
||||
func (a *arrayHashMap) entrySet() []*entry {
|
||||
var pairs []*entry
|
||||
for _, pair := range a.bucket {
|
||||
if pair != nil {
|
||||
pairs = append(pairs, pair)
|
||||
@ -65,7 +65,7 @@ func (a *ArrayHashMap) entrySet() []*Entry {
|
||||
}
|
||||
|
||||
/* 获取所有键 */
|
||||
func (a *ArrayHashMap) keySet() []int {
|
||||
func (a *arrayHashMap) keySet() []int {
|
||||
var keys []int
|
||||
for _, pair := range a.bucket {
|
||||
if pair != nil {
|
||||
@ -76,7 +76,7 @@ func (a *ArrayHashMap) keySet() []int {
|
||||
}
|
||||
|
||||
/* 获取所有值 */
|
||||
func (a *ArrayHashMap) valueSet() []string {
|
||||
func (a *arrayHashMap) valueSet() []string {
|
||||
var values []string
|
||||
for _, pair := range a.bucket {
|
||||
if pair != nil {
|
||||
@ -87,7 +87,7 @@ func (a *ArrayHashMap) valueSet() []string {
|
||||
}
|
||||
|
||||
/* 打印哈希表 */
|
||||
func (a *ArrayHashMap) print() {
|
||||
func (a *arrayHashMap) print() {
|
||||
for _, pair := range a.bucket {
|
||||
if pair != nil {
|
||||
fmt.Println(pair.key, "->", pair.val)
|
||||
|
@ -6,8 +6,9 @@ package chapter_searching
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/krahets/hello-algo/pkg"
|
||||
"testing"
|
||||
|
||||
. "github.com/krahets/hello-algo/pkg"
|
||||
)
|
||||
|
||||
func TestHashingSearch(t *testing.T) {
|
||||
|
@ -14,19 +14,19 @@ func merge(nums []int, left, mid, right int) {
|
||||
tmp[i-left] = nums[i]
|
||||
}
|
||||
// 左子数组的起始索引和结束索引
|
||||
left_start, left_end := left-left, mid-left
|
||||
leftStart, leftEnd := left-left, mid-left
|
||||
// 右子数组的起始索引和结束索引
|
||||
right_start, right_end := mid+1-left, right-left
|
||||
rightStart, rightEnd := mid+1-left, right-left
|
||||
// i, j 分别指向左子数组、右子数组的首元素
|
||||
i, j := left_start, right_start
|
||||
i, j := leftStart, rightStart
|
||||
// 通过覆盖原数组 nums 来合并左子数组和右子数组
|
||||
for k := left; k <= right; k++ {
|
||||
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
|
||||
if i > left_end {
|
||||
if i > leftEnd {
|
||||
nums[k] = tmp[j]
|
||||
j++
|
||||
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
|
||||
} else if j > right_end || tmp[i] <= tmp[j] {
|
||||
} else if j > rightEnd || tmp[i] <= tmp[j] {
|
||||
nums[k] = tmp[i]
|
||||
i++
|
||||
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
|
||||
|
@ -5,16 +5,16 @@
|
||||
package chapter_sorting
|
||||
|
||||
// 快速排序
|
||||
type QuickSort struct{}
|
||||
type quickSort struct{}
|
||||
|
||||
// 快速排序(中位基准数优化)
|
||||
type QuickSortMedian struct{}
|
||||
type quickSortMedian struct{}
|
||||
|
||||
// 快速排序(尾递归优化)
|
||||
type QuickSortTailCall struct{}
|
||||
type quickSortTailCall struct{}
|
||||
|
||||
/* 哨兵划分 */
|
||||
func (q *QuickSort) partition(nums []int, left, right int) int {
|
||||
func (q *quickSort) partition(nums []int, left, right int) int {
|
||||
// 以 nums[left] 作为基准数
|
||||
i, j := left, right
|
||||
for i < j {
|
||||
@ -33,7 +33,7 @@ func (q *QuickSort) partition(nums []int, left, right int) int {
|
||||
}
|
||||
|
||||
/* 快速排序 */
|
||||
func (q *QuickSort) quickSort(nums []int, left, right int) {
|
||||
func (q *quickSort) quickSort(nums []int, left, right int) {
|
||||
// 子数组长度为 1 时终止递归
|
||||
if left >= right {
|
||||
return
|
||||
@ -46,7 +46,7 @@ func (q *QuickSort) quickSort(nums []int, left, right int) {
|
||||
}
|
||||
|
||||
/* 选取三个元素的中位数 */
|
||||
func (q *QuickSortMedian) medianThree(nums []int, left, mid, right int) int {
|
||||
func (q *quickSortMedian) 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]) {
|
||||
@ -56,7 +56,7 @@ func (q *QuickSortMedian) medianThree(nums []int, left, mid, right int) int {
|
||||
}
|
||||
|
||||
/* 哨兵划分(三数取中值)*/
|
||||
func (q *QuickSortMedian) partition(nums []int, left, right int) int {
|
||||
func (q *quickSortMedian) partition(nums []int, left, right int) int {
|
||||
// 以 nums[left] 作为基准数
|
||||
med := q.medianThree(nums, left, (left+right)/2, right)
|
||||
// 将中位数交换至数组最左端
|
||||
@ -79,7 +79,7 @@ func (q *QuickSortMedian) partition(nums []int, left, right int) int {
|
||||
}
|
||||
|
||||
/* 快速排序 */
|
||||
func (q *QuickSortMedian) quickSort(nums []int, left, right int) {
|
||||
func (q *quickSortMedian) quickSort(nums []int, left, right int) {
|
||||
// 子数组长度为 1 时终止递归
|
||||
if left >= right {
|
||||
return
|
||||
@ -92,7 +92,7 @@ func (q *QuickSortMedian) quickSort(nums []int, left, right int) {
|
||||
}
|
||||
|
||||
/* 哨兵划分 */
|
||||
func (q *QuickSortTailCall) partition(nums []int, left, right int) int {
|
||||
func (q *quickSortTailCall) partition(nums []int, left, right int) int {
|
||||
// 以 nums[left] 作为基准数
|
||||
i, j := left, right
|
||||
for i < j {
|
||||
@ -111,7 +111,7 @@ func (q *QuickSortTailCall) partition(nums []int, left, right int) int {
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化)*/
|
||||
func (q *QuickSortTailCall) quickSort(nums []int, left, right int) {
|
||||
func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
|
||||
// 子数组长度为 1 时终止
|
||||
for left < right {
|
||||
// 哨兵划分操作
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// 快速排序
|
||||
func TestQuickSort(t *testing.T) {
|
||||
q := QuickSort{}
|
||||
q := quickSort{}
|
||||
nums := []int{4, 1, 3, 1, 5, 2}
|
||||
q.quickSort(nums, 0, len(nums)-1)
|
||||
fmt.Println("快速排序完成后 nums = ", nums)
|
||||
@ -19,7 +19,7 @@ func TestQuickSort(t *testing.T) {
|
||||
|
||||
// 快速排序(中位基准数优化)
|
||||
func TestQuickSortMedian(t *testing.T) {
|
||||
q := QuickSortMedian{}
|
||||
q := quickSortMedian{}
|
||||
nums := []int{4, 1, 3, 1, 5, 2}
|
||||
q.quickSort(nums, 0, len(nums)-1)
|
||||
fmt.Println("快速排序(中位基准数优化)完成后 nums = ", nums)
|
||||
@ -27,7 +27,7 @@ func TestQuickSortMedian(t *testing.T) {
|
||||
|
||||
// 快速排序(尾递归优化)
|
||||
func TestQuickSortTailCall(t *testing.T) {
|
||||
q := QuickSortTailCall{}
|
||||
q := quickSortTailCall{}
|
||||
nums := []int{4, 1, 3, 1, 5, 2}
|
||||
q.quickSort(nums, 0, len(nums)-1)
|
||||
fmt.Println("快速排序(尾递归优化)完成后 nums = ", nums)
|
||||
|
@ -5,16 +5,16 @@
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 基于环形数组实现的队列 */
|
||||
type ArrayQueue struct {
|
||||
type arrayQueue struct {
|
||||
data []int // 用于存储队列元素的数组
|
||||
capacity int // 队列容量(即最多容量的元素个数)
|
||||
front int // 头指针,指向队首
|
||||
rear int // 尾指针,指向队尾 + 1
|
||||
}
|
||||
|
||||
// NewArrayQueue 基于环形数组实现的队列
|
||||
func NewArrayQueue(capacity int) *ArrayQueue {
|
||||
return &ArrayQueue{
|
||||
// newArrayQueue 基于环形数组实现的队列
|
||||
func newArrayQueue(capacity int) *arrayQueue {
|
||||
return &arrayQueue{
|
||||
data: make([]int, capacity),
|
||||
capacity: capacity,
|
||||
front: 0,
|
||||
@ -22,21 +22,21 @@ func NewArrayQueue(capacity int) *ArrayQueue {
|
||||
}
|
||||
}
|
||||
|
||||
// Size 获取队列的长度
|
||||
func (q *ArrayQueue) Size() int {
|
||||
// size 获取队列的长度
|
||||
func (q *arrayQueue) size() int {
|
||||
size := (q.capacity + q.rear - q.front) % q.capacity
|
||||
return size
|
||||
}
|
||||
|
||||
// IsEmpty 判断队列是否为空
|
||||
func (q *ArrayQueue) IsEmpty() bool {
|
||||
// isEmpty 判断队列是否为空
|
||||
func (q *arrayQueue) isEmpty() bool {
|
||||
return q.rear-q.front == 0
|
||||
}
|
||||
|
||||
// Offer 入队
|
||||
func (q *ArrayQueue) Offer(v int) {
|
||||
// offer 入队
|
||||
func (q *arrayQueue) offer(v int) {
|
||||
// 当 rear == capacity 表示队列已满
|
||||
if q.Size() == q.capacity {
|
||||
if q.size() == q.capacity {
|
||||
return
|
||||
}
|
||||
// 尾结点后添加
|
||||
@ -45,9 +45,9 @@ func (q *ArrayQueue) Offer(v int) {
|
||||
q.rear = (q.rear + 1) % q.capacity
|
||||
}
|
||||
|
||||
// Poll 出队
|
||||
func (q *ArrayQueue) Poll() any {
|
||||
if q.IsEmpty() {
|
||||
// poll 出队
|
||||
func (q *arrayQueue) poll() any {
|
||||
if q.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
v := q.data[q.front]
|
||||
@ -56,9 +56,9 @@ func (q *ArrayQueue) Poll() any {
|
||||
return v
|
||||
}
|
||||
|
||||
// Peek 访问队首元素
|
||||
func (q *ArrayQueue) Peek() any {
|
||||
if q.IsEmpty() {
|
||||
// peek 访问队首元素
|
||||
func (q *arrayQueue) peek() any {
|
||||
if q.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
v := q.data[q.front]
|
||||
@ -66,6 +66,6 @@ func (q *ArrayQueue) Peek() any {
|
||||
}
|
||||
|
||||
// 获取 Slice 用于打印
|
||||
func (s *ArrayQueue) toSlice() []int {
|
||||
return s.data[s.front:s.rear]
|
||||
func (q *arrayQueue) toSlice() []int {
|
||||
return q.data[q.front:q.rear]
|
||||
}
|
||||
|
@ -5,47 +5,47 @@
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 基于数组实现的栈 */
|
||||
type ArrayStack struct {
|
||||
type arrayStack struct {
|
||||
data []int // 数据
|
||||
}
|
||||
|
||||
func NewArrayStack() *ArrayStack {
|
||||
return &ArrayStack{
|
||||
func newArrayStack() *arrayStack {
|
||||
return &arrayStack{
|
||||
// 设置栈的长度为 0,容量为 16
|
||||
data: make([]int, 0, 16),
|
||||
}
|
||||
}
|
||||
|
||||
// Size 栈的长度
|
||||
func (s *ArrayStack) Size() int {
|
||||
// size 栈的长度
|
||||
func (s *arrayStack) size() int {
|
||||
return len(s.data)
|
||||
}
|
||||
|
||||
// IsEmpty 栈是否为空
|
||||
func (s *ArrayStack) IsEmpty() bool {
|
||||
return s.Size() == 0
|
||||
// isEmpty 栈是否为空
|
||||
func (s *arrayStack) isEmpty() bool {
|
||||
return s.size() == 0
|
||||
}
|
||||
|
||||
// Push 入栈
|
||||
func (s *ArrayStack) Push(v int) {
|
||||
// push 入栈
|
||||
func (s *arrayStack) push(v int) {
|
||||
// 切片会自动扩容
|
||||
s.data = append(s.data, v)
|
||||
}
|
||||
|
||||
// Pop 出栈
|
||||
func (s *ArrayStack) Pop() any {
|
||||
// pop 出栈
|
||||
func (s *arrayStack) pop() any {
|
||||
// 弹出栈前,先判断是否为空
|
||||
if s.IsEmpty() {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
val := s.Peek()
|
||||
val := s.peek()
|
||||
s.data = s.data[:len(s.data)-1]
|
||||
return val
|
||||
}
|
||||
|
||||
// Peek 获取栈顶元素
|
||||
func (s *ArrayStack) Peek() any {
|
||||
if s.IsEmpty() {
|
||||
// peek 获取栈顶元素
|
||||
func (s *arrayStack) peek() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
val := s.data[len(s.data)-1]
|
||||
@ -53,6 +53,6 @@ func (s *ArrayStack) Peek() any {
|
||||
}
|
||||
|
||||
// 获取 Slice 用于打印
|
||||
func (s *ArrayStack) toSlice() []int {
|
||||
func (s *arrayStack) toSlice() []int {
|
||||
return s.data
|
||||
}
|
||||
|
@ -51,48 +51,48 @@ func TestDeque(t *testing.T) {
|
||||
|
||||
func TestLinkedListDeque(t *testing.T) {
|
||||
// 初始化队列
|
||||
deque := NewLinkedListDeque()
|
||||
deque := newLinkedListDeque()
|
||||
|
||||
// 元素入队
|
||||
deque.OfferLast(2)
|
||||
deque.OfferLast(5)
|
||||
deque.OfferLast(4)
|
||||
deque.OfferFirst(3)
|
||||
deque.OfferFirst(1)
|
||||
deque.offerLast(2)
|
||||
deque.offerLast(5)
|
||||
deque.offerLast(4)
|
||||
deque.offerFirst(3)
|
||||
deque.offerFirst(1)
|
||||
fmt.Print("队列 deque = ")
|
||||
PrintList(deque.toList())
|
||||
|
||||
// 访问队首元素
|
||||
front := deque.PeekFirst()
|
||||
front := deque.peekFirst()
|
||||
fmt.Println("队首元素 front =", front)
|
||||
rear := deque.PeekLast()
|
||||
rear := deque.peekLast()
|
||||
fmt.Println("队尾元素 rear =", rear)
|
||||
|
||||
// 元素出队
|
||||
pollFirst := deque.PollFirst()
|
||||
pollFirst := deque.pollFirst()
|
||||
fmt.Print("队首出队元素 pollFirst = ", pollFirst, ",队首出队后 deque = ")
|
||||
PrintList(deque.toList())
|
||||
pollLast := deque.PollLast()
|
||||
pollLast := deque.pollLast()
|
||||
fmt.Print("队尾出队元素 pollLast = ", pollLast, ",队尾出队后 deque = ")
|
||||
PrintList(deque.toList())
|
||||
|
||||
// 获取队的长度
|
||||
size := deque.Size()
|
||||
size := deque.size()
|
||||
fmt.Println("队的长度 size =", size)
|
||||
|
||||
// 判断是否为空
|
||||
isEmpty := deque.IsEmpty()
|
||||
isEmpty := deque.isEmpty()
|
||||
fmt.Println("队是否为空 =", isEmpty)
|
||||
}
|
||||
|
||||
// BenchmarkArrayQueue 67.92 ns/op in Mac M1 Pro
|
||||
func BenchmarkLinkedListDeque(b *testing.B) {
|
||||
stack := NewLinkedListDeque()
|
||||
stack := newLinkedListDeque()
|
||||
// use b.N for looping
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.OfferLast(777)
|
||||
stack.offerLast(777)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.PollFirst()
|
||||
stack.pollFirst()
|
||||
}
|
||||
}
|
||||
|
@ -8,31 +8,31 @@ import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
// LinkedListDeque 基于链表实现的双端队列, 使用内置包 list 来实现栈
|
||||
type LinkedListDeque struct {
|
||||
// linkedListDeque 基于链表实现的双端队列, 使用内置包 list 来实现栈
|
||||
type linkedListDeque struct {
|
||||
data *list.List
|
||||
}
|
||||
|
||||
// NewLinkedListDeque 初始化双端队列
|
||||
func NewLinkedListDeque() *LinkedListDeque {
|
||||
return &LinkedListDeque{
|
||||
// newLinkedListDeque 初始化双端队列
|
||||
func newLinkedListDeque() *linkedListDeque {
|
||||
return &linkedListDeque{
|
||||
data: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// OfferFirst 队首元素入队
|
||||
func (s *LinkedListDeque) OfferFirst(value any) {
|
||||
// offerFirst 队首元素入队
|
||||
func (s *linkedListDeque) offerFirst(value any) {
|
||||
s.data.PushFront(value)
|
||||
}
|
||||
|
||||
// OfferLast 队尾元素入队
|
||||
func (s *LinkedListDeque) OfferLast(value any) {
|
||||
// offerLast 队尾元素入队
|
||||
func (s *linkedListDeque) offerLast(value any) {
|
||||
s.data.PushBack(value)
|
||||
}
|
||||
|
||||
// PollFirst 队首元素出队
|
||||
func (s *LinkedListDeque) PollFirst() any {
|
||||
if s.IsEmpty() {
|
||||
// pollFirst 队首元素出队
|
||||
func (s *linkedListDeque) pollFirst() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Front()
|
||||
@ -40,9 +40,9 @@ func (s *LinkedListDeque) PollFirst() any {
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// PollLast 队尾元素出队
|
||||
func (s *LinkedListDeque) PollLast() any {
|
||||
if s.IsEmpty() {
|
||||
// pollLast 队尾元素出队
|
||||
func (s *linkedListDeque) pollLast() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Back()
|
||||
@ -50,35 +50,35 @@ func (s *LinkedListDeque) PollLast() any {
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// PeekFirst 访问队首元素
|
||||
func (s *LinkedListDeque) PeekFirst() any {
|
||||
if s.IsEmpty() {
|
||||
// peekFirst 访问队首元素
|
||||
func (s *linkedListDeque) peekFirst() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Front()
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// PeekLast 访问队尾元素
|
||||
func (s *LinkedListDeque) PeekLast() any {
|
||||
if s.IsEmpty() {
|
||||
// peekLast 访问队尾元素
|
||||
func (s *linkedListDeque) peekLast() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Back()
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// Size 获取队列的长度
|
||||
func (s *LinkedListDeque) Size() int {
|
||||
// size 获取队列的长度
|
||||
func (s *linkedListDeque) size() int {
|
||||
return s.data.Len()
|
||||
}
|
||||
|
||||
// IsEmpty 判断队列是否为空
|
||||
func (s *LinkedListDeque) IsEmpty() bool {
|
||||
// isEmpty 判断队列是否为空
|
||||
func (s *linkedListDeque) isEmpty() bool {
|
||||
return s.data.Len() == 0
|
||||
}
|
||||
|
||||
// 获取 List 用于打印
|
||||
func (s *LinkedListDeque) toList() *list.List {
|
||||
func (s *linkedListDeque) toList() *list.List {
|
||||
return s.data
|
||||
}
|
||||
|
@ -9,26 +9,26 @@ import (
|
||||
)
|
||||
|
||||
/* 基于链表实现的队列 */
|
||||
type LinkedListQueue struct {
|
||||
type linkedListQueue struct {
|
||||
// 使用内置包 list 来实现队列
|
||||
data *list.List
|
||||
}
|
||||
|
||||
// NewLinkedListQueue 初始化链表
|
||||
func NewLinkedListQueue() *LinkedListQueue {
|
||||
return &LinkedListQueue{
|
||||
// newLinkedListQueue 初始化链表
|
||||
func newLinkedListQueue() *linkedListQueue {
|
||||
return &linkedListQueue{
|
||||
data: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Offer 入队
|
||||
func (s *LinkedListQueue) Offer(value any) {
|
||||
// offer 入队
|
||||
func (s *linkedListQueue) offer(value any) {
|
||||
s.data.PushBack(value)
|
||||
}
|
||||
|
||||
// Poll 出队
|
||||
func (s *LinkedListQueue) Poll() any {
|
||||
if s.IsEmpty() {
|
||||
// poll 出队
|
||||
func (s *linkedListQueue) poll() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Front()
|
||||
@ -36,26 +36,26 @@ func (s *LinkedListQueue) Poll() any {
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// Peek 访问队首元素
|
||||
func (s *LinkedListQueue) Peek() any {
|
||||
if s.IsEmpty() {
|
||||
// peek 访问队首元素
|
||||
func (s *linkedListQueue) peek() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Front()
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// Size 获取队列的长度
|
||||
func (s *LinkedListQueue) Size() int {
|
||||
// size 获取队列的长度
|
||||
func (s *linkedListQueue) size() int {
|
||||
return s.data.Len()
|
||||
}
|
||||
|
||||
// IsEmpty 判断队列是否为空
|
||||
func (s *LinkedListQueue) IsEmpty() bool {
|
||||
// isEmpty 判断队列是否为空
|
||||
func (s *linkedListQueue) isEmpty() bool {
|
||||
return s.data.Len() == 0
|
||||
}
|
||||
|
||||
// 获取 List 用于打印
|
||||
func (s *LinkedListQueue) toList() *list.List {
|
||||
func (s *linkedListQueue) toList() *list.List {
|
||||
return s.data
|
||||
}
|
||||
|
@ -9,26 +9,26 @@ import (
|
||||
)
|
||||
|
||||
/* 基于链表实现的栈 */
|
||||
type LinkedListStack struct {
|
||||
type linkedListStack struct {
|
||||
// 使用内置包 list 来实现栈
|
||||
data *list.List
|
||||
}
|
||||
|
||||
// NewLinkedListStack 初始化链表
|
||||
func NewLinkedListStack() *LinkedListStack {
|
||||
return &LinkedListStack{
|
||||
// newLinkedListStack 初始化链表
|
||||
func newLinkedListStack() *linkedListStack {
|
||||
return &linkedListStack{
|
||||
data: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Push 入栈
|
||||
func (s *LinkedListStack) Push(value int) {
|
||||
// push 入栈
|
||||
func (s *linkedListStack) push(value int) {
|
||||
s.data.PushBack(value)
|
||||
}
|
||||
|
||||
// Pop 出栈
|
||||
func (s *LinkedListStack) Pop() any {
|
||||
if s.IsEmpty() {
|
||||
// pop 出栈
|
||||
func (s *linkedListStack) pop() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Back()
|
||||
@ -36,26 +36,26 @@ func (s *LinkedListStack) Pop() any {
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// Peek 访问栈顶元素
|
||||
func (s *LinkedListStack) Peek() any {
|
||||
if s.IsEmpty() {
|
||||
// peek 访问栈顶元素
|
||||
func (s *linkedListStack) peek() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
e := s.data.Back()
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// Size 获取栈的长度
|
||||
func (s *LinkedListStack) Size() int {
|
||||
// size 获取栈的长度
|
||||
func (s *linkedListStack) size() int {
|
||||
return s.data.Len()
|
||||
}
|
||||
|
||||
// IsEmpty 判断栈是否为空
|
||||
func (s *LinkedListStack) IsEmpty() bool {
|
||||
// isEmpty 判断栈是否为空
|
||||
func (s *linkedListStack) isEmpty() bool {
|
||||
return s.data.Len() == 0
|
||||
}
|
||||
|
||||
// 获取 List 用于打印
|
||||
func (s *LinkedListStack) toList() *list.List {
|
||||
func (s *linkedListStack) toList() *list.List {
|
||||
return s.data
|
||||
}
|
||||
|
@ -48,87 +48,87 @@ func TestQueue(t *testing.T) {
|
||||
func TestArrayQueue(t *testing.T) {
|
||||
// 初始化队列,使用队列的通用接口
|
||||
capacity := 10
|
||||
queue := NewArrayQueue(capacity)
|
||||
queue := newArrayQueue(capacity)
|
||||
|
||||
// 元素入队
|
||||
queue.Offer(1)
|
||||
queue.Offer(3)
|
||||
queue.Offer(2)
|
||||
queue.Offer(5)
|
||||
queue.Offer(4)
|
||||
queue.offer(1)
|
||||
queue.offer(3)
|
||||
queue.offer(2)
|
||||
queue.offer(5)
|
||||
queue.offer(4)
|
||||
fmt.Print("队列 queue = ")
|
||||
PrintSlice(queue.toSlice())
|
||||
|
||||
// 访问队首元素
|
||||
peek := queue.Peek()
|
||||
peek := queue.peek()
|
||||
fmt.Println("队首元素 peek =", peek)
|
||||
|
||||
// 元素出队
|
||||
poll := queue.Poll()
|
||||
poll := queue.poll()
|
||||
fmt.Print("出队元素 poll = ", poll, ", 出队后 queue = ")
|
||||
PrintSlice(queue.toSlice())
|
||||
|
||||
// 获取队的长度
|
||||
size := queue.Size()
|
||||
size := queue.size()
|
||||
fmt.Println("队的长度 size =", size)
|
||||
|
||||
// 判断是否为空
|
||||
isEmpty := queue.IsEmpty()
|
||||
isEmpty := queue.isEmpty()
|
||||
fmt.Println("队是否为空 =", isEmpty)
|
||||
}
|
||||
|
||||
func TestLinkedListQueue(t *testing.T) {
|
||||
// 初始化队
|
||||
queue := NewLinkedListQueue()
|
||||
queue := newLinkedListQueue()
|
||||
|
||||
// 元素入队
|
||||
queue.Offer(1)
|
||||
queue.Offer(3)
|
||||
queue.Offer(2)
|
||||
queue.Offer(5)
|
||||
queue.Offer(4)
|
||||
queue.offer(1)
|
||||
queue.offer(3)
|
||||
queue.offer(2)
|
||||
queue.offer(5)
|
||||
queue.offer(4)
|
||||
fmt.Print("队列 queue = ")
|
||||
PrintList(queue.toList())
|
||||
|
||||
// 访问队首元素
|
||||
peek := queue.Peek()
|
||||
peek := queue.peek()
|
||||
fmt.Println("队首元素 peek =", peek)
|
||||
|
||||
// 元素出队
|
||||
poll := queue.Poll()
|
||||
poll := queue.poll()
|
||||
fmt.Print("出队元素 poll = ", poll, ", 出队后 queue = ")
|
||||
PrintList(queue.toList())
|
||||
|
||||
// 获取队的长度
|
||||
size := queue.Size()
|
||||
size := queue.size()
|
||||
fmt.Println("队的长度 size =", size)
|
||||
|
||||
// 判断是否为空
|
||||
isEmpty := queue.IsEmpty()
|
||||
isEmpty := queue.isEmpty()
|
||||
fmt.Println("队是否为空 =", isEmpty)
|
||||
}
|
||||
|
||||
// BenchmarkArrayQueue 8 ns/op in Mac M1 Pro
|
||||
func BenchmarkArrayQueue(b *testing.B) {
|
||||
capacity := 1000
|
||||
stack := NewArrayQueue(capacity)
|
||||
stack := newArrayQueue(capacity)
|
||||
// use b.N for looping
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Offer(777)
|
||||
stack.offer(777)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Poll()
|
||||
stack.poll()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkLinkedQueue 62.66 ns/op in Mac M1 Pro
|
||||
func BenchmarkLinkedQueue(b *testing.B) {
|
||||
stack := NewLinkedListQueue()
|
||||
stack := newLinkedListQueue()
|
||||
// use b.N for looping
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Offer(777)
|
||||
stack.offer(777)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Poll()
|
||||
stack.poll()
|
||||
}
|
||||
}
|
||||
|
@ -46,85 +46,85 @@ func TestStack(t *testing.T) {
|
||||
|
||||
func TestArrayStack(t *testing.T) {
|
||||
// 初始化栈, 使用接口承接
|
||||
stack := NewArrayStack()
|
||||
stack := newArrayStack()
|
||||
|
||||
// 元素入栈
|
||||
stack.Push(1)
|
||||
stack.Push(3)
|
||||
stack.Push(2)
|
||||
stack.Push(5)
|
||||
stack.Push(4)
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
fmt.Print("栈 stack = ")
|
||||
PrintSlice(stack.toSlice())
|
||||
|
||||
// 访问栈顶元素
|
||||
peek := stack.Peek()
|
||||
peek := stack.peek()
|
||||
fmt.Println("栈顶元素 peek =", peek)
|
||||
|
||||
// 元素出栈
|
||||
pop := stack.Pop()
|
||||
pop := stack.pop()
|
||||
fmt.Print("出栈元素 pop = ", pop, ", 出栈后 stack = ")
|
||||
PrintSlice(stack.toSlice())
|
||||
|
||||
// 获取栈的长度
|
||||
size := stack.Size()
|
||||
size := stack.size()
|
||||
fmt.Println("栈的长度 size =", size)
|
||||
|
||||
// 判断是否为空
|
||||
isEmpty := stack.IsEmpty()
|
||||
isEmpty := stack.isEmpty()
|
||||
fmt.Println("栈是否为空 =", isEmpty)
|
||||
}
|
||||
|
||||
func TestLinkedListStack(t *testing.T) {
|
||||
// 初始化栈
|
||||
stack := NewLinkedListStack()
|
||||
stack := newLinkedListStack()
|
||||
// 元素入栈
|
||||
stack.Push(1)
|
||||
stack.Push(3)
|
||||
stack.Push(2)
|
||||
stack.Push(5)
|
||||
stack.Push(4)
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
fmt.Print("栈 stack = ")
|
||||
PrintList(stack.toList())
|
||||
|
||||
// 访问栈顶元素
|
||||
peek := stack.Peek()
|
||||
peek := stack.peek()
|
||||
fmt.Println("栈顶元素 peek =", peek)
|
||||
|
||||
// 元素出栈
|
||||
pop := stack.Pop()
|
||||
pop := stack.pop()
|
||||
fmt.Print("出栈元素 pop = ", pop, ", 出栈后 stack = ")
|
||||
PrintList(stack.toList())
|
||||
|
||||
// 获取栈的长度
|
||||
size := stack.Size()
|
||||
size := stack.size()
|
||||
fmt.Println("栈的长度 size =", size)
|
||||
|
||||
// 判断是否为空
|
||||
isEmpty := stack.IsEmpty()
|
||||
isEmpty := stack.isEmpty()
|
||||
fmt.Println("栈是否为空 =", isEmpty)
|
||||
}
|
||||
|
||||
// BenchmarkArrayStack 8 ns/op in Mac M1 Pro
|
||||
func BenchmarkArrayStack(b *testing.B) {
|
||||
stack := NewArrayStack()
|
||||
stack := newArrayStack()
|
||||
// use b.N for looping
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Push(777)
|
||||
stack.push(777)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Pop()
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkLinkedListStack 65.02 ns/op in Mac M1 Pro
|
||||
func BenchmarkLinkedListStack(b *testing.B) {
|
||||
stack := NewLinkedListStack()
|
||||
stack := newLinkedListStack()
|
||||
// use b.N for looping
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Push(777)
|
||||
stack.push(777)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
stack.Pop()
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
|
211
codes/go/chapter_tree/avl_tree.go
Normal file
211
codes/go/chapter_tree/avl_tree.go
Normal file
@ -0,0 +1,211 @@
|
||||
// File: avl_tree.go
|
||||
// Created Time: 2023-01-08
|
||||
// Author: Reanon (793584285@qq.com)
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import . "github.com/krahets/hello-algo/pkg"
|
||||
|
||||
/* AVL Tree*/
|
||||
type avlTree struct {
|
||||
// 根节点
|
||||
root *TreeNode
|
||||
}
|
||||
|
||||
func newAVLTree() *avlTree {
|
||||
return &avlTree{root: nil}
|
||||
}
|
||||
|
||||
/* 获取结点高度 */
|
||||
func height(node *TreeNode) int {
|
||||
// 空结点高度为 -1 ,叶结点高度为 0
|
||||
if node != nil {
|
||||
return node.Height
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/* 更新结点高度 */
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取平衡因子 */
|
||||
func balanceFactor(node *TreeNode) int {
|
||||
// 空结点平衡因子为 0
|
||||
if node == nil {
|
||||
return 0
|
||||
}
|
||||
// 结点平衡因子 = 左子树高度 - 右子树高度
|
||||
return height(node.Left) - height(node.Right)
|
||||
}
|
||||
|
||||
/* 右旋操作 */
|
||||
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
|
||||
}
|
||||
|
||||
/* 左旋操作 */
|
||||
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
|
||||
}
|
||||
|
||||
/* 执行旋转操作,使该子树重新恢复平衡 */
|
||||
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
|
||||
}
|
||||
|
||||
/* 插入结点 */
|
||||
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
|
||||
}
|
||||
|
||||
/* 删除结点 */
|
||||
func (t *avlTree) remove(val int) *TreeNode {
|
||||
root := removeHelper(t.root, val)
|
||||
return root
|
||||
}
|
||||
|
||||
/* 递归删除结点(辅助函数) */
|
||||
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
|
||||
}
|
||||
|
||||
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
|
||||
func getInOrderNext(node *TreeNode) *TreeNode {
|
||||
if node == nil {
|
||||
return node
|
||||
}
|
||||
// 循环访问左子结点,直到叶结点时为最小结点,跳出
|
||||
for node.Left != nil {
|
||||
node = node.Left
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
/* 查找结点 */
|
||||
func (t *avlTree) search(val int) *TreeNode {
|
||||
cur := t.root
|
||||
// 循环查找,越过叶结点后跳出
|
||||
for cur != nil {
|
||||
// 目标结点在 root 的右子树中
|
||||
if cur.Val < val {
|
||||
cur = cur.Right
|
||||
} else if cur.Val > val {
|
||||
// 目标结点在 root 的左子树中
|
||||
cur = cur.Left
|
||||
} else {
|
||||
// 找到目标结点,跳出循环
|
||||
break
|
||||
}
|
||||
}
|
||||
// 返回目标结点
|
||||
return cur
|
||||
}
|
54
codes/go/chapter_tree/avl_tree_test.go
Normal file
54
codes/go/chapter_tree/avl_tree_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
// File: avl_tree_test.go
|
||||
// Created Time: 2023-01-08
|
||||
// Author: Reanon (793584285@qq.com)
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
. "github.com/krahets/hello-algo/pkg"
|
||||
)
|
||||
|
||||
func TestAVLTree(t *testing.T) {
|
||||
/* 初始化空 AVL 树 */
|
||||
tree := 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 的结点
|
||||
|
||||
/* 查询结点 */
|
||||
node := tree.search(7)
|
||||
fmt.Printf("\n查找到的结点对象为 %#v ,结点值 = %d \n", node, node.Val)
|
||||
}
|
||||
|
||||
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) {
|
||||
tree.remove(val)
|
||||
fmt.Printf("\n删除结点 %d 后,AVL 树为 \n", val)
|
||||
PrintTree(tree.root)
|
||||
}
|
@ -10,26 +10,26 @@ import (
|
||||
. "github.com/krahets/hello-algo/pkg"
|
||||
)
|
||||
|
||||
type BinarySearchTree struct {
|
||||
type binarySearchTree struct {
|
||||
root *TreeNode
|
||||
}
|
||||
|
||||
func NewBinarySearchTree(nums []int) *BinarySearchTree {
|
||||
func newBinarySearchTree(nums []int) *binarySearchTree {
|
||||
// sorting array
|
||||
sort.Ints(nums)
|
||||
root := buildBinarySearchTree(nums, 0, len(nums)-1)
|
||||
return &BinarySearchTree{
|
||||
return &binarySearchTree{
|
||||
root: root,
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取根结点 */
|
||||
func (bst *BinarySearchTree) GetRoot() *TreeNode {
|
||||
func (bst *binarySearchTree) getRoot() *TreeNode {
|
||||
return bst.root
|
||||
}
|
||||
|
||||
/* 获取中序遍历的下一个结点 */
|
||||
func (bst *BinarySearchTree) GetInOrderNext(node *TreeNode) *TreeNode {
|
||||
func (bst *binarySearchTree) getInOrderNext(node *TreeNode) *TreeNode {
|
||||
if node == nil {
|
||||
return node
|
||||
}
|
||||
@ -41,7 +41,7 @@ func (bst *BinarySearchTree) GetInOrderNext(node *TreeNode) *TreeNode {
|
||||
}
|
||||
|
||||
/* 查找结点 */
|
||||
func (bst *BinarySearchTree) Search(num int) *TreeNode {
|
||||
func (bst *binarySearchTree) search(num int) *TreeNode {
|
||||
node := bst.root
|
||||
// 循环查找,越过叶结点后跳出
|
||||
for node != nil {
|
||||
@ -61,7 +61,7 @@ func (bst *BinarySearchTree) Search(num int) *TreeNode {
|
||||
}
|
||||
|
||||
/* 插入结点 */
|
||||
func (bst *BinarySearchTree) Insert(num int) *TreeNode {
|
||||
func (bst *binarySearchTree) insert(num int) *TreeNode {
|
||||
cur := bst.root
|
||||
// 若树为空,直接提前返回
|
||||
if cur == nil {
|
||||
@ -92,7 +92,7 @@ func (bst *BinarySearchTree) Insert(num int) *TreeNode {
|
||||
}
|
||||
|
||||
/* 删除结点 */
|
||||
func (bst *BinarySearchTree) Remove(num int) *TreeNode {
|
||||
func (bst *binarySearchTree) remove(num int) *TreeNode {
|
||||
cur := bst.root
|
||||
// 若树为空,直接提前返回
|
||||
if cur == nil {
|
||||
@ -136,10 +136,10 @@ func (bst *BinarySearchTree) Remove(num int) *TreeNode {
|
||||
// 子结点数为 2
|
||||
} else {
|
||||
// 获取中序遍历中待删除结点 cur 的下一个结点
|
||||
next := bst.GetInOrderNext(cur)
|
||||
next := bst.getInOrderNext(cur)
|
||||
temp := next.Val
|
||||
// 递归删除结点 next
|
||||
bst.Remove(next.Val)
|
||||
bst.remove(next.Val)
|
||||
// 将 next 的值复制给 cur
|
||||
cur.Val = temp
|
||||
}
|
||||
@ -160,7 +160,7 @@ func buildBinarySearchTree(nums []int, left, right int) *TreeNode {
|
||||
return root
|
||||
}
|
||||
|
||||
// Print binary search tree
|
||||
func (bst *BinarySearchTree) Print() {
|
||||
// print binary search tree
|
||||
func (bst *binarySearchTree) print() {
|
||||
PrintTree(bst.root)
|
||||
}
|
||||
|
@ -11,31 +11,31 @@ import (
|
||||
|
||||
func TestBinarySearchTree(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
bst := NewBinarySearchTree(nums)
|
||||
bst := newBinarySearchTree(nums)
|
||||
fmt.Println("\n初始化的二叉树为:")
|
||||
bst.Print()
|
||||
bst.print()
|
||||
|
||||
// 获取根结点
|
||||
node := bst.GetRoot()
|
||||
node := bst.getRoot()
|
||||
fmt.Println("\n二叉树的根结点为:", node.Val)
|
||||
|
||||
// 查找结点
|
||||
node = bst.Search(5)
|
||||
fmt.Println("\n查找到的结点对象为", node, ",结点值 =", node.Val)
|
||||
node = bst.search(7)
|
||||
fmt.Println("查找到的结点对象为", node, ",结点值 =", node.Val)
|
||||
|
||||
// 插入结点
|
||||
node = bst.Insert(16)
|
||||
node = bst.insert(16)
|
||||
fmt.Println("\n插入结点后 16 的二叉树为:")
|
||||
bst.Print()
|
||||
bst.print()
|
||||
|
||||
// 删除结点
|
||||
bst.Remove(1)
|
||||
bst.remove(1)
|
||||
fmt.Println("\n删除结点 1 后的二叉树为:")
|
||||
bst.Print()
|
||||
bst.Remove(2)
|
||||
bst.print()
|
||||
bst.remove(2)
|
||||
fmt.Println("\n删除结点 2 后的二叉树为:")
|
||||
bst.Print()
|
||||
bst.Remove(4)
|
||||
bst.print()
|
||||
bst.remove(4)
|
||||
fmt.Println("\n删除结点 4 后的二叉树为:")
|
||||
bst.Print()
|
||||
bst.print()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
func TestLevelOrder(t *testing.T) {
|
||||
/* 初始化二叉树 */
|
||||
// 这里借助了一个从数组直接生成二叉树的函数
|
||||
root := ArrToTree([]int{1, 2, 3, 4, 5, 6, 7})
|
||||
root := ArrToTree([]any{1, 2, 3, 4, 5, 6, 7})
|
||||
fmt.Println("\n初始化二叉树: ")
|
||||
PrintTree(root)
|
||||
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
func TestPreInPostOrderTraversal(t *testing.T) {
|
||||
/* 初始化二叉树 */
|
||||
// 这里借助了一个从数组直接生成二叉树的函数
|
||||
root := ArrToTree([]int{1, 2, 3, 4, 5, 6, 7})
|
||||
root := ArrToTree([]any{1, 2, 3, 4, 5, 6, 7})
|
||||
fmt.Println("\n初始化二叉树: ")
|
||||
PrintTree(root)
|
||||
|
||||
|
@ -76,7 +76,7 @@ func printTreeHelper(root *TreeNode, prev *trunk, isLeft bool) {
|
||||
printTreeHelper(root.Left, trunk, false)
|
||||
}
|
||||
|
||||
// trunk Help to Print tree structure
|
||||
// trunk Help to print tree structure
|
||||
type trunk struct {
|
||||
prev *trunk
|
||||
str string
|
||||
|
@ -9,25 +9,28 @@ import (
|
||||
)
|
||||
|
||||
type TreeNode struct {
|
||||
Val int
|
||||
Left *TreeNode
|
||||
Right *TreeNode
|
||||
Val int // 结点值
|
||||
Height int // 结点高度
|
||||
Left *TreeNode // 左子结点引用
|
||||
Right *TreeNode // 右子结点引用
|
||||
}
|
||||
|
||||
func NewTreeNode(v int) *TreeNode {
|
||||
return &TreeNode{
|
||||
Val: v,
|
||||
Height: 0,
|
||||
Left: nil,
|
||||
Right: nil,
|
||||
Val: v,
|
||||
}
|
||||
}
|
||||
|
||||
// ArrToTree Generate a binary tree given an array
|
||||
func ArrToTree(arr []int) *TreeNode {
|
||||
func ArrToTree(arr []any) *TreeNode {
|
||||
if len(arr) <= 0 {
|
||||
return nil
|
||||
}
|
||||
root := NewTreeNode(arr[0])
|
||||
// TreeNode only accept integer value for now.
|
||||
root := NewTreeNode(arr[0].(int))
|
||||
// Let container.list as queue
|
||||
queue := list.New()
|
||||
queue.PushBack(root)
|
||||
@ -37,15 +40,19 @@ func ArrToTree(arr []int) *TreeNode {
|
||||
node := queue.Remove(queue.Front()).(*TreeNode)
|
||||
i++
|
||||
if i < len(arr) {
|
||||
node.Left = NewTreeNode(arr[i])
|
||||
if arr[i] != nil {
|
||||
node.Left = NewTreeNode(arr[i].(int))
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
}
|
||||
i++
|
||||
if i < len(arr) {
|
||||
node.Right = NewTreeNode(arr[i])
|
||||
if arr[i] != nil {
|
||||
node.Right = NewTreeNode(arr[i].(int))
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestTreeNode(t *testing.T) {
|
||||
arr := []int{2, 3, 5, 6, 7}
|
||||
arr := []any{1, 2, 3, nil, 5, 6, nil}
|
||||
node := ArrToTree(arr)
|
||||
|
||||
// print tree
|
||||
|
@ -29,9 +29,9 @@ public class linked_list {
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
static ListNode access(ListNode head, int index) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
head = head.next;
|
||||
if (head == null)
|
||||
return null;
|
||||
head = head.next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public class binary_search_tree {
|
||||
PrintUtil.printTree(bst.getRoot());
|
||||
|
||||
/* 查找结点 */
|
||||
TreeNode node = bst.search(5);
|
||||
TreeNode node = bst.search(7);
|
||||
System.out.println("\n查找到的结点对象为 " + node + ",结点值 = " + node.val);
|
||||
|
||||
/* 插入结点 */
|
||||
|
51
codes/javascript/chapter_searching/hashing_search.js
Normal file
51
codes/javascript/chapter_searching/hashing_search.js
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: hashing_search.js
|
||||
* Created Time: 2022-12-29
|
||||
* Author: Zhuo Qinyue (1403450829@qq.com)
|
||||
*/
|
||||
|
||||
const PrintUtil = require("../include/PrintUtil");
|
||||
const ListNode = require("../include/ListNode");
|
||||
|
||||
|
||||
/* 哈希查找(数组) */
|
||||
function hashingSearch(map, target) {
|
||||
// 哈希表的 key: 目标元素,value: 索引
|
||||
// 若哈希表中无此 key ,返回 -1
|
||||
return map.has(target) ? map.get(target) : -1;
|
||||
}
|
||||
|
||||
/* 哈希查找(链表) */
|
||||
function hashingSearch1(map, target) {
|
||||
// 哈希表的 key: 目标结点值,value: 结点对象
|
||||
// 若哈希表中无此 key ,返回 null
|
||||
return map.has(target) ? map.get(target) : null;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const target = 3;
|
||||
|
||||
/* 哈希查找(数组) */
|
||||
const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8];
|
||||
// 初始化哈希表
|
||||
const map = new Map();
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
map.set(nums[i], i); // key: 元素,value: 索引
|
||||
}
|
||||
const index = hashingSearch(map, target);
|
||||
console.log("目标元素 3 的索引 = " + index);
|
||||
|
||||
/* 哈希查找(链表) */
|
||||
let head = new ListNode().arrToLinkedList(nums)
|
||||
// 初始化哈希表
|
||||
const map1 = new Map();
|
||||
while (head != null) {
|
||||
map1.set(head.val, head); // key: 结点值,value: 结点
|
||||
head = head.next;
|
||||
}
|
||||
const node = hashingSearch1(map1, target);
|
||||
console.log("目标结点值 3 的对应结点对象为" );
|
||||
PrintUtil.printLinkedList(node);
|
||||
}
|
||||
|
||||
main();
|
@ -126,7 +126,7 @@ console.log("\n初始化的二叉树为\n");
|
||||
printTree(getRoot());
|
||||
|
||||
/* 查找结点 */
|
||||
let node = search(5);
|
||||
let node = search(7);
|
||||
console.log("\n查找到的结点对象为 " + node + ",结点值 = " + node.val);
|
||||
|
||||
/* 插入结点 */
|
||||
|
@ -26,9 +26,9 @@ def remove(n0):
|
||||
""" 访问链表中索引为 index 的结点 """
|
||||
def access(head, index):
|
||||
for _ in range(index):
|
||||
head = head.next
|
||||
if not head:
|
||||
return None
|
||||
head = head.next
|
||||
return head
|
||||
|
||||
""" 在链表中查找值为 target 的首个结点 """
|
||||
|
@ -5,30 +5,28 @@ Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys, os.path as osp
|
||||
import typing
|
||||
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__))))
|
||||
from include import *
|
||||
|
||||
|
||||
class AVLTree:
|
||||
def __init__(self, root: typing.Optional[TreeNode] = None):
|
||||
def __init__(self, root: Optional[TreeNode] = None):
|
||||
self.root = root
|
||||
|
||||
""" 获取结点高度 """
|
||||
def height(self, node: typing.Optional[TreeNode]) -> int:
|
||||
def height(self, node: Optional[TreeNode]) -> int:
|
||||
# 空结点高度为 -1 ,叶结点高度为 0
|
||||
if node is not None:
|
||||
return node.height
|
||||
return -1
|
||||
|
||||
""" 更新结点高度 """
|
||||
def __update_height(self, node: TreeNode):
|
||||
def __update_height(self, node: Optional[TreeNode]):
|
||||
# 结点高度等于最高子树高度 + 1
|
||||
node.height = max([self.height(node.left), self.height(node.right)]) + 1
|
||||
|
||||
""" 获取平衡因子 """
|
||||
def balance_factor(self, node: TreeNode) -> int:
|
||||
def balance_factor(self, node: Optional[TreeNode]) -> int:
|
||||
# 空结点平衡因子为 0
|
||||
if node is None:
|
||||
return 0
|
||||
@ -36,7 +34,7 @@ class AVLTree:
|
||||
return self.height(node.left) - self.height(node.right)
|
||||
|
||||
""" 右旋操作 """
|
||||
def __right_rotate(self, node: TreeNode) -> TreeNode:
|
||||
def __right_rotate(self, node: Optional[TreeNode]) -> TreeNode:
|
||||
child = node.left
|
||||
grand_child = child.right
|
||||
# 以 child 为原点,将 node 向右旋转
|
||||
@ -49,7 +47,7 @@ class AVLTree:
|
||||
return child
|
||||
|
||||
""" 左旋操作 """
|
||||
def __left_rotate(self, node: TreeNode) -> TreeNode:
|
||||
def __left_rotate(self, node: Optional[TreeNode]) -> TreeNode:
|
||||
child = node.right
|
||||
grand_child = child.left
|
||||
# 以 child 为原点,将 node 向左旋转
|
||||
@ -62,7 +60,7 @@ class AVLTree:
|
||||
return child
|
||||
|
||||
""" 执行旋转操作,使该子树重新恢复平衡 """
|
||||
def __rotate(self, node: TreeNode) -> TreeNode:
|
||||
def __rotate(self, node: Optional[TreeNode]) -> TreeNode:
|
||||
# 获取结点 node 的平衡因子
|
||||
balance_factor = self.balance_factor(node)
|
||||
# 左偏树
|
||||
@ -92,7 +90,7 @@ class AVLTree:
|
||||
return self.root
|
||||
|
||||
""" 递归插入结点(辅助函数)"""
|
||||
def __insert_helper(self, node: typing.Optional[TreeNode], val: int) -> TreeNode:
|
||||
def __insert_helper(self, node: Optional[TreeNode], val: int) -> TreeNode:
|
||||
if node is None:
|
||||
return TreeNode(val)
|
||||
# 1. 查找插入位置,并插入结点
|
||||
@ -114,7 +112,7 @@ class AVLTree:
|
||||
return root
|
||||
|
||||
""" 递归删除结点(辅助函数) """
|
||||
def __remove_helper(self, node: typing.Optional[TreeNode], val: int) -> typing.Optional[TreeNode]:
|
||||
def __remove_helper(self, node: Optional[TreeNode], val: int) -> Optional[TreeNode]:
|
||||
if node is None:
|
||||
return None
|
||||
# 1. 查找结点,并删除之
|
||||
@ -141,7 +139,7 @@ class AVLTree:
|
||||
return self.__rotate(node)
|
||||
|
||||
""" 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """
|
||||
def __get_inorder_next(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]:
|
||||
def __get_inorder_next(self, node: Optional[TreeNode]) -> Optional[TreeNode]:
|
||||
if node is None:
|
||||
return None
|
||||
# 循环访问左子结点,直到叶结点时为最小结点,跳出
|
||||
|
@ -5,20 +5,18 @@ Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys, os.path as osp
|
||||
import typing
|
||||
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__))))
|
||||
from include import *
|
||||
|
||||
|
||||
""" 二叉搜索树 """
|
||||
class BinarySearchTree:
|
||||
def __init__(self, nums: typing.List[int]) -> None:
|
||||
def __init__(self, nums: List[int]) -> None:
|
||||
nums.sort()
|
||||
self.__root = self.build_tree(nums, 0, len(nums) - 1)
|
||||
|
||||
""" 构建二叉搜索树 """
|
||||
def build_tree(self, nums: typing.List[int], start_index: int, end_index: int) -> typing.Optional[TreeNode]:
|
||||
def build_tree(self, nums: List[int], start_index: int, end_index: int) -> Optional[TreeNode]:
|
||||
if start_index > end_index:
|
||||
return None
|
||||
|
||||
@ -31,11 +29,11 @@ class BinarySearchTree:
|
||||
return root
|
||||
|
||||
@property
|
||||
def root(self) -> typing.Optional[TreeNode]:
|
||||
def root(self) -> Optional[TreeNode]:
|
||||
return self.__root
|
||||
|
||||
""" 查找结点 """
|
||||
def search(self, num: int) -> typing.Optional[TreeNode]:
|
||||
def search(self, num: int) -> Optional[TreeNode]:
|
||||
cur = self.root
|
||||
# 循环查找,越过叶结点后跳出
|
||||
while cur is not None:
|
||||
@ -51,7 +49,7 @@ class BinarySearchTree:
|
||||
return cur
|
||||
|
||||
""" 插入结点 """
|
||||
def insert(self, num: int) -> typing.Optional[TreeNode]:
|
||||
def insert(self, num: int) -> Optional[TreeNode]:
|
||||
root = self.root
|
||||
# 若树为空,直接提前返回
|
||||
if root is None:
|
||||
@ -81,7 +79,7 @@ class BinarySearchTree:
|
||||
return node
|
||||
|
||||
""" 删除结点 """
|
||||
def remove(self, num: int) -> typing.Optional[TreeNode]:
|
||||
def remove(self, num: int) -> Optional[TreeNode]:
|
||||
root = self.root
|
||||
# 若树为空,直接提前返回
|
||||
if root is None:
|
||||
@ -126,7 +124,7 @@ class BinarySearchTree:
|
||||
return cur
|
||||
|
||||
""" 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """
|
||||
def get_inorder_next(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]:
|
||||
def get_inorder_next(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
|
||||
if root is None:
|
||||
return root
|
||||
# 循环访问左子结点,直到叶结点时为最小结点,跳出
|
||||
@ -144,7 +142,7 @@ if __name__ == "__main__":
|
||||
print_tree(bst.root)
|
||||
|
||||
# 查找结点
|
||||
node = bst.search(5)
|
||||
node = bst.search(7)
|
||||
print("\n查找到的结点对象为: {},结点值 = {}".format(node, node.val))
|
||||
|
||||
# 插入结点
|
||||
|
@ -5,14 +5,12 @@ Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys, os.path as osp
|
||||
import typing
|
||||
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__))))
|
||||
from include import *
|
||||
|
||||
|
||||
""" 层序遍历 """
|
||||
def hier_order(root: TreeNode):
|
||||
def hier_order(root: Optional[TreeNode]):
|
||||
# 初始化队列,加入根结点
|
||||
queue = collections.deque()
|
||||
queue.append(root)
|
||||
|
@ -5,8 +5,6 @@ Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys, os.path as osp
|
||||
import typing
|
||||
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__))))
|
||||
from include import *
|
||||
|
||||
@ -14,7 +12,7 @@ from include import *
|
||||
res = []
|
||||
|
||||
""" 前序遍历 """
|
||||
def pre_order(root: typing.Optional[TreeNode]):
|
||||
def pre_order(root: Optional[TreeNode]):
|
||||
if root is None:
|
||||
return
|
||||
# 访问优先级:根结点 -> 左子树 -> 右子树
|
||||
@ -23,7 +21,7 @@ def pre_order(root: typing.Optional[TreeNode]):
|
||||
pre_order(root=root.right)
|
||||
|
||||
""" 中序遍历 """
|
||||
def in_order(root: typing.Optional[TreeNode]):
|
||||
def in_order(root: Optional[TreeNode]):
|
||||
if root is None:
|
||||
return
|
||||
# 访问优先级:左子树 -> 根结点 -> 右子树
|
||||
@ -32,7 +30,7 @@ def in_order(root: typing.Optional[TreeNode]):
|
||||
in_order(root=root.right)
|
||||
|
||||
""" 后序遍历 """
|
||||
def post_order(root: typing.Optional[TreeNode]):
|
||||
def post_order(root: Optional[TreeNode]):
|
||||
if root is None:
|
||||
return
|
||||
# 访问优先级:左子树 -> 右子树 -> 根结点
|
||||
|
@ -4,7 +4,7 @@ import queue
|
||||
import random
|
||||
import functools
|
||||
import collections
|
||||
from typing import List
|
||||
from typing import Optional, List, Dict, DefaultDict, OrderedDict, Set, Deque
|
||||
from .linked_list import ListNode, list_to_linked_list, linked_list_to_list, get_list_node
|
||||
from .binary_tree import TreeNode, list_to_tree, tree_to_list, get_tree_node
|
||||
from .print_util import print_matrix, print_linked_list, print_tree, print_dict
|
163
codes/rust/chapter_computational_complexity/time_complexity.rs
Normal file
163
codes/rust/chapter_computational_complexity/time_complexity.rs
Normal file
@ -0,0 +1,163 @@
|
||||
#![allow(unused_variables)]
|
||||
|
||||
/* 常数阶 */
|
||||
fn constant(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
let size = 100000;
|
||||
for _ in 0..size {
|
||||
count += 1
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn linear(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
for _ in 0..n {
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* 线性阶(遍历数组) */
|
||||
fn array_traversal(nums: &[i32]) -> i32 {
|
||||
let mut count = 0;
|
||||
// 循环次数与数组长度成正比
|
||||
for _ in nums {
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn quadratic(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
// 循环次数与数组长度成平方关系
|
||||
for _ in 0..n {
|
||||
for _ in 0..n {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* 平方阶(冒泡排序) */
|
||||
fn bubble_sort(nums: &mut [i32]) -> i32 {
|
||||
let mut count = 0; // 计数器
|
||||
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for i in (1..nums.len()).rev() {
|
||||
// 内循环:冒泡操作
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
let tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
count += 3; // 元素交换包含 3 个单元操作
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* 指数阶(循环实现) */
|
||||
fn exponential(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
let mut base = 1;
|
||||
// cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1)
|
||||
for _ in 0..n {
|
||||
for _ in 0..base {
|
||||
count += 1
|
||||
}
|
||||
base *= 2;
|
||||
}
|
||||
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
count
|
||||
}
|
||||
|
||||
/* 指数阶(递归实现) */
|
||||
fn exp_recur(n: i32) -> i32 {
|
||||
if n == 1 {
|
||||
return 1;
|
||||
}
|
||||
exp_recur(n - 1) + exp_recur(n - 1) + 1
|
||||
}
|
||||
|
||||
/* 对数阶(循环实现) */
|
||||
fn logarithmic(mut n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
|
||||
while n > 1 {
|
||||
n = n / 2;
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn log_recur(n: i32) -> i32 {
|
||||
if n <= 1 {
|
||||
return 0;
|
||||
}
|
||||
log_recur(n / 2) + 1
|
||||
}
|
||||
|
||||
/* 线性对数阶 */
|
||||
fn linear_log_recur(n: f64) -> i32 {
|
||||
if n <= 1.0 {
|
||||
return 1;
|
||||
}
|
||||
let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0);
|
||||
for _ in 0 ..n as i32 {
|
||||
count += 1;
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 阶乘阶(递归实现) */
|
||||
fn factorial_recur(n: i32) -> i32 {
|
||||
if n == 0 {
|
||||
return 1;
|
||||
}
|
||||
let mut count = 0;
|
||||
// 从 1 个分裂出 n 个
|
||||
for _ in 0..n {
|
||||
count += factorial_recur(n - 1);
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势
|
||||
let n: i32 = 8;
|
||||
println!("输入数据大小 n = {}", n);
|
||||
|
||||
let mut count = constant(n);
|
||||
println!("常数阶的计算操作数量 = {}", count);
|
||||
|
||||
count = linear(n);
|
||||
println!("线性阶的计算操作数量 = {}", count);
|
||||
count = array_traversal(&vec![0; n as usize]);
|
||||
println!("线性阶(遍历数组)的计算操作数量 = {}", count);
|
||||
|
||||
count = quadratic(n);
|
||||
println!("平方阶的计算操作数量 = {}", count);
|
||||
|
||||
let mut nums = (1..=n).rev().collect::<Vec<_>>(); // [n,n-1,...,2,1]
|
||||
count = bubble_sort(&mut nums);
|
||||
println!("平方阶(冒泡排序)的计算操作数量 = {}", count);
|
||||
|
||||
count = exponential(n);
|
||||
println!("指数阶(循环实现)的计算操作数量 = {}", count);
|
||||
count = exp_recur(n);
|
||||
println!("指数阶(递归实现)的计算操作数量 = {}", count);
|
||||
|
||||
count = logarithmic(n);
|
||||
println!("对数阶(循环实现)的计算操作数量 = {}", count);
|
||||
count = log_recur(n);
|
||||
println!("对数阶(递归实现)的计算操作数量 = {}", count);
|
||||
|
||||
count = linear_log_recur(n.into());
|
||||
println!("线性对数阶(递归实现)的计算操作数量 = {}", count);
|
||||
|
||||
count = factorial_recur(n);
|
||||
println!("阶乘阶(递归实现)的计算操作数量 = {}", count);
|
||||
}
|
@ -10,6 +10,12 @@ let package = Package(
|
||||
.executable(name: "space_complexity", targets: ["space_complexity"]),
|
||||
.executable(name: "leetcode_two_sum", targets: ["leetcode_two_sum"]),
|
||||
.executable(name: "array", targets: ["array"]),
|
||||
.executable(name: "linked_list", targets: ["linked_list"]),
|
||||
.executable(name: "list", targets: ["list"]),
|
||||
.executable(name: "my_list", targets: ["my_list"]),
|
||||
.executable(name: "stack", targets: ["stack"]),
|
||||
.executable(name: "linkedlist_stack", targets: ["linkedlist_stack"]),
|
||||
.executable(name: "array_stack", targets: ["array_stack"]),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "utils", path: "utils"),
|
||||
@ -18,5 +24,11 @@ let package = Package(
|
||||
.executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]),
|
||||
.executableTarget(name: "leetcode_two_sum", path: "chapter_computational_complexity", sources: ["leetcode_two_sum.swift"]),
|
||||
.executableTarget(name: "array", path: "chapter_array_and_linkedlist", sources: ["array.swift"]),
|
||||
.executableTarget(name: "linked_list", dependencies: ["utils"], path: "chapter_array_and_linkedlist", sources: ["linked_list.swift"]),
|
||||
.executableTarget(name: "list", path: "chapter_array_and_linkedlist", sources: ["list.swift"]),
|
||||
.executableTarget(name: "my_list", path: "chapter_array_and_linkedlist", sources: ["my_list.swift"]),
|
||||
.executableTarget(name: "stack", path: "chapter_stack_and_queue", sources: ["stack.swift"]),
|
||||
.executableTarget(name: "linkedlist_stack", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_stack.swift"]),
|
||||
.executableTarget(name: "array_stack", path: "chapter_stack_and_queue", sources: ["array_stack.swift"]),
|
||||
]
|
||||
)
|
||||
|
91
codes/swift/chapter_array_and_linkedlist/linked_list.swift
Normal file
91
codes/swift/chapter_array_and_linkedlist/linked_list.swift
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* File: linked_list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 在链表的结点 n0 之后插入结点 P */
|
||||
func insert(n0: ListNode, P: ListNode) {
|
||||
let n1 = n0.next
|
||||
n0.next = P
|
||||
P.next = n1
|
||||
}
|
||||
|
||||
/* 删除链表的结点 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
|
||||
}
|
||||
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
func access(head: ListNode, index: Int) -> ListNode? {
|
||||
var head: ListNode? = head
|
||||
for _ in 0 ..< index {
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
head = head?.next
|
||||
}
|
||||
return head
|
||||
}
|
||||
|
||||
/* 在链表中查找值为 target 的首个结点 */
|
||||
func find(head: ListNode, target: Int) -> Int {
|
||||
var head: ListNode? = head
|
||||
var index = 0
|
||||
while head != nil {
|
||||
if head?.val == target {
|
||||
return index
|
||||
}
|
||||
head = head?.next
|
||||
index += 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum LinkedList {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 初始化链表 */
|
||||
// 初始化各个结点
|
||||
let n0 = ListNode(x: 1)
|
||||
let n1 = ListNode(x: 3)
|
||||
let n2 = ListNode(x: 2)
|
||||
let n3 = ListNode(x: 5)
|
||||
let n4 = ListNode(x: 4)
|
||||
// 构建引用指向
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
print("初始化的链表为")
|
||||
PrintUtil.printLinkedList(head: n0)
|
||||
|
||||
/* 插入结点 */
|
||||
insert(n0: n0, P: ListNode(x: 0))
|
||||
print("插入结点后的链表为")
|
||||
PrintUtil.printLinkedList(head: n0)
|
||||
|
||||
/* 删除结点 */
|
||||
remove(n0: n0)
|
||||
print("删除结点后的链表为")
|
||||
PrintUtil.printLinkedList(head: n0)
|
||||
|
||||
/* 访问结点 */
|
||||
let node = access(head: n0, index: 3)
|
||||
print("链表中索引 3 处的结点的值 = \(node!.val)")
|
||||
|
||||
/* 查找结点 */
|
||||
let index = find(head: n0, target: 2)
|
||||
print("链表中值为 2 的结点的索引 = \(index)")
|
||||
}
|
||||
}
|
64
codes/swift/chapter_array_and_linkedlist/list.swift
Normal file
64
codes/swift/chapter_array_and_linkedlist/list.swift
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* File: list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
@main
|
||||
enum List {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 初始化列表 */
|
||||
var list = [1, 3, 2, 5, 4]
|
||||
print("列表 list = \(list)")
|
||||
|
||||
/* 访问元素 */
|
||||
let num = list[1]
|
||||
print("访问索引 1 处的元素,得到 num = \(num)")
|
||||
|
||||
/* 更新元素 */
|
||||
list[1] = 0
|
||||
print("将索引 1 处的元素更新为 0 ,得到 list = \(list)")
|
||||
|
||||
/* 清空列表 */
|
||||
list.removeAll()
|
||||
print("清空列表后 list = \(list)")
|
||||
|
||||
/* 尾部添加元素 */
|
||||
list.append(1)
|
||||
list.append(3)
|
||||
list.append(2)
|
||||
list.append(5)
|
||||
list.append(4)
|
||||
print("添加元素后 list = \(list)")
|
||||
|
||||
/* 中间插入元素 */
|
||||
list.insert(6, at: 3)
|
||||
print("在索引 3 处插入数字 6 ,得到 list = \(list)")
|
||||
|
||||
/* 删除元素 */
|
||||
list.remove(at: 3)
|
||||
print("删除索引 3 处的元素,得到 list = \(list)")
|
||||
|
||||
/* 通过索引遍历列表 */
|
||||
var count = 0
|
||||
for _ in list.indices {
|
||||
count += 1
|
||||
}
|
||||
|
||||
/* 直接遍历列表元素 */
|
||||
count = 0
|
||||
for _ in list {
|
||||
count += 1
|
||||
}
|
||||
|
||||
/* 拼接两个列表 */
|
||||
let list1 = [6, 8, 7, 10, 9]
|
||||
list.append(contentsOf: list1)
|
||||
print("将列表 list1 拼接到 list 之后,得到 list = \(list)")
|
||||
|
||||
/* 排序列表 */
|
||||
list.sort()
|
||||
print("排序列表后 list = \(list)")
|
||||
}
|
||||
}
|
147
codes/swift/chapter_array_and_linkedlist/my_list.swift
Normal file
147
codes/swift/chapter_array_and_linkedlist/my_list.swift
Normal file
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* File: my_list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 列表类简易实现 */
|
||||
class MyList {
|
||||
private var nums: [Int] // 数组(存储列表元素)
|
||||
private var _capacity = 10 // 列表容量
|
||||
private var _size = 0 // 列表长度(即当前元素数量)
|
||||
private let extendRatio = 2 // 每次列表扩容的倍数
|
||||
|
||||
/* 构造函数 */
|
||||
init() {
|
||||
nums = Array(repeating: 0, count: _capacity)
|
||||
}
|
||||
|
||||
/* 获取列表长度(即当前元素数量)*/
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* 获取列表容量 */
|
||||
func capacity() -> Int {
|
||||
_capacity
|
||||
}
|
||||
|
||||
/* 访问元素 */
|
||||
func get(index: Int) -> Int {
|
||||
// 索引如果越界则抛出错误,下同
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
return nums[index]
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
func set(index: Int, num: Int) {
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
/* 尾部添加元素 */
|
||||
func add(num: Int) {
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if _size == _capacity {
|
||||
extendCapacity()
|
||||
}
|
||||
nums[_size] = num
|
||||
// 更新元素数量
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 中间插入元素 */
|
||||
func insert(index: Int, num: Int) {
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if _size == _capacity {
|
||||
extendCapacity()
|
||||
}
|
||||
// 将索引 index 以及之后的元素都向后移动一位
|
||||
for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) {
|
||||
nums[j + 1] = nums[j]
|
||||
}
|
||||
nums[index] = num
|
||||
// 更新元素数量
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 删除元素 */
|
||||
@discardableResult
|
||||
func remove(index: Int) -> Int {
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
let num = nums[index]
|
||||
// 将索引 index 之后的元素都向前移动一位
|
||||
for j in index ..< (_size - 1) {
|
||||
nums[j] = nums[j + 1]
|
||||
}
|
||||
// 更新元素数量
|
||||
_size -= 1
|
||||
// 返回被删除元素
|
||||
return num
|
||||
}
|
||||
|
||||
/* 列表扩容 */
|
||||
func extendCapacity() {
|
||||
// 新建一个长度为 size 的数组,并将原数组拷贝到新数组
|
||||
nums = nums + Array(repeating: 0, count: _capacity * (extendRatio - 1))
|
||||
// 更新列表容量
|
||||
_capacity = nums.count
|
||||
}
|
||||
|
||||
/* 将列表转换为数组 */
|
||||
func toArray() -> [Int] {
|
||||
var nums = Array(repeating: 0, count: _size)
|
||||
for i in 0 ..< _size {
|
||||
nums[i] = get(index: i)
|
||||
}
|
||||
return nums
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _MyList {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 初始化列表 */
|
||||
let list = MyList()
|
||||
/* 尾部添加元素 */
|
||||
list.add(num: 1)
|
||||
list.add(num: 3)
|
||||
list.add(num: 2)
|
||||
list.add(num: 5)
|
||||
list.add(num: 4)
|
||||
print("列表 list = \(list.toArray()) ,容量 = \(list.capacity()) ,长度 = \(list.size())")
|
||||
|
||||
/* 中间插入元素 */
|
||||
list.insert(index: 3, num: 6)
|
||||
print("在索引 3 处插入数字 6 ,得到 list = \(list.toArray())")
|
||||
|
||||
/* 删除元素 */
|
||||
list.remove(index: 3)
|
||||
print("删除索引 3 处的元素,得到 list = \(list.toArray())")
|
||||
|
||||
/* 访问元素 */
|
||||
let num = list.get(index: 1)
|
||||
print("访问索引 1 处的元素,得到 num = \(num)")
|
||||
|
||||
/* 更新元素 */
|
||||
list.set(index: 1, num: 0)
|
||||
print("将索引 1 处的元素更新为 0 ,得到 list = \(list.toArray())")
|
||||
|
||||
/* 测试扩容机制 */
|
||||
for i in 0 ..< 10 {
|
||||
// 在 i = 5 时,列表长度将超出列表容量,此时触发扩容机制
|
||||
list.add(num: i)
|
||||
}
|
||||
print("扩容后的列表 list = \(list.toArray()) ,容量 = \(list.capacity()) ,长度 = \(list.size())")
|
||||
}
|
||||
}
|
@ -6,14 +6,14 @@
|
||||
|
||||
import utils
|
||||
|
||||
// 函数
|
||||
/* 函数 */
|
||||
@discardableResult
|
||||
func function() -> Int {
|
||||
// do something
|
||||
return 0
|
||||
}
|
||||
|
||||
// 常数阶
|
||||
/* 常数阶 */
|
||||
func constant(n: Int) {
|
||||
// 常量、变量、对象占用 O(1) 空间
|
||||
let a = 0
|
||||
@ -30,7 +30,7 @@ func constant(n: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
// 线性阶
|
||||
/* 线性阶 */
|
||||
func linear(n: Int) {
|
||||
// 长度为 n 的数组占用 O(n) 空间
|
||||
let nums = Array(repeating: 0, count: n)
|
||||
@ -40,7 +40,7 @@ func linear(n: Int) {
|
||||
let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") })
|
||||
}
|
||||
|
||||
// 线性阶(递归实现)
|
||||
/* 线性阶(递归实现) */
|
||||
func linearRecur(n: Int) {
|
||||
print("递归 n = \(n)")
|
||||
if n == 1 {
|
||||
@ -49,13 +49,13 @@ func linearRecur(n: Int) {
|
||||
linearRecur(n: n - 1)
|
||||
}
|
||||
|
||||
// 平方阶
|
||||
/* 平方阶 */
|
||||
func quadratic(n: Int) {
|
||||
// 二维列表占用 O(n^2) 空间
|
||||
let numList = Array(repeating: Array(repeating: 0, count: n), count: n)
|
||||
}
|
||||
|
||||
// 平方阶(递归实现)
|
||||
/* 平方阶(递归实现) */
|
||||
@discardableResult
|
||||
func quadraticRecur(n: Int) -> Int {
|
||||
if n <= 0 {
|
||||
@ -67,7 +67,7 @@ func quadraticRecur(n: Int) -> Int {
|
||||
return quadraticRecur(n: n - 1)
|
||||
}
|
||||
|
||||
// 指数阶(建立满二叉树)
|
||||
/* 指数阶(建立满二叉树) */
|
||||
func buildTree(n: Int) -> TreeNode? {
|
||||
if n == 0 {
|
||||
return nil
|
||||
@ -80,7 +80,7 @@ func buildTree(n: Int) -> TreeNode? {
|
||||
|
||||
@main
|
||||
enum SpaceComplexity {
|
||||
// Driver Code
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 5
|
||||
// 常数阶
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
// 常数阶
|
||||
/* 常数阶 */
|
||||
func constant(n: Int) -> Int {
|
||||
var count = 0
|
||||
let size = 100_000
|
||||
@ -14,7 +14,7 @@ func constant(n: Int) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 线性阶
|
||||
/* 线性阶 */
|
||||
func linear(n: Int) -> Int {
|
||||
var count = 0
|
||||
for _ in 0 ..< n {
|
||||
@ -23,7 +23,7 @@ func linear(n: Int) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 线性阶(遍历数组)
|
||||
/* 线性阶(遍历数组) */
|
||||
func arrayTraversal(nums: [Int]) -> Int {
|
||||
var count = 0
|
||||
// 循环次数与数组长度成正比
|
||||
@ -33,7 +33,7 @@ func arrayTraversal(nums: [Int]) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 平方阶
|
||||
/* 平方阶 */
|
||||
func quadratic(n: Int) -> Int {
|
||||
var count = 0
|
||||
// 循环次数与数组长度成平方关系
|
||||
@ -45,11 +45,11 @@ func quadratic(n: Int) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 平方阶(冒泡排序)
|
||||
/* 平方阶(冒泡排序) */
|
||||
func bubbleSort(nums: inout [Int]) -> Int {
|
||||
var count = 0 // 计数器
|
||||
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for i in sequence(first: nums.count - 1, next: { $0 > 0 ? $0 - 1 : nil }) {
|
||||
for i in sequence(first: nums.count - 1, next: { $0 > 0 + 1 ? $0 - 1 : nil }) {
|
||||
// 内循环:冒泡操作
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
@ -64,7 +64,7 @@ func bubbleSort(nums: inout [Int]) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 指数阶(循环实现)
|
||||
/* 指数阶(循环实现) */
|
||||
func exponential(n: Int) -> Int {
|
||||
var count = 0
|
||||
var base = 1
|
||||
@ -79,7 +79,7 @@ func exponential(n: Int) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 指数阶(递归实现)
|
||||
/* 指数阶(递归实现) */
|
||||
func expRecur(n: Int) -> Int {
|
||||
if n == 1 {
|
||||
return 1
|
||||
@ -87,7 +87,7 @@ func expRecur(n: Int) -> Int {
|
||||
return expRecur(n: n - 1) + expRecur(n: n - 1) + 1
|
||||
}
|
||||
|
||||
// 对数阶(循环实现)
|
||||
/* 对数阶(循环实现) */
|
||||
func logarithmic(n: Int) -> Int {
|
||||
var count = 0
|
||||
var n = n
|
||||
@ -98,7 +98,7 @@ func logarithmic(n: Int) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 对数阶(递归实现)
|
||||
/* 对数阶(递归实现) */
|
||||
func logRecur(n: Int) -> Int {
|
||||
if n <= 1 {
|
||||
return 0
|
||||
@ -106,7 +106,7 @@ func logRecur(n: Int) -> Int {
|
||||
return logRecur(n: n / 2) + 1
|
||||
}
|
||||
|
||||
// 线性对数阶
|
||||
/* 线性对数阶 */
|
||||
func linearLogRecur(n: Double) -> Int {
|
||||
if n <= 1 {
|
||||
return 1
|
||||
@ -118,7 +118,7 @@ func linearLogRecur(n: Double) -> Int {
|
||||
return count
|
||||
}
|
||||
|
||||
// 阶乘阶(递归实现)
|
||||
/* 阶乘阶(递归实现) */
|
||||
func factorialRecur(n: Int) -> Int {
|
||||
if n == 0 {
|
||||
return 1
|
||||
@ -133,39 +133,40 @@ func factorialRecur(n: Int) -> Int {
|
||||
|
||||
@main
|
||||
enum TimeComplexity {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势
|
||||
let n = 8
|
||||
print("输入数据大小 n =", n)
|
||||
print("输入数据大小 n = \(n)")
|
||||
|
||||
var count = constant(n: n)
|
||||
print("常数阶的计算操作数量 =", count)
|
||||
print("常数阶的计算操作数量 = \(count)")
|
||||
|
||||
count = linear(n: n)
|
||||
print("线性阶的计算操作数量 =", count)
|
||||
print("线性阶的计算操作数量 = \(count)")
|
||||
count = arrayTraversal(nums: Array(repeating: 0, count: n))
|
||||
print("线性阶(遍历数组)的计算操作数量 =", count)
|
||||
print("线性阶(遍历数组)的计算操作数量 = \(count)")
|
||||
|
||||
count = quadratic(n: n)
|
||||
print("平方阶的计算操作数量 =", count)
|
||||
var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1]
|
||||
print("平方阶的计算操作数量 = \(count)")
|
||||
var nums = Array(sequence(first: n, next: { $0 > 0 + 1 ? $0 - 1 : nil })) // [n,n-1,...,2,1]
|
||||
count = bubbleSort(nums: &nums)
|
||||
print("平方阶(冒泡排序)的计算操作数量 =", count)
|
||||
print("平方阶(冒泡排序)的计算操作数量 = \(count)")
|
||||
|
||||
count = exponential(n: n)
|
||||
print("指数阶(循环实现)的计算操作数量 =", count)
|
||||
print("指数阶(循环实现)的计算操作数量 = \(count)")
|
||||
count = expRecur(n: n)
|
||||
print("指数阶(递归实现)的计算操作数量 =", count)
|
||||
print("指数阶(递归实现)的计算操作数量 = \(count)")
|
||||
|
||||
count = logarithmic(n: n)
|
||||
print("对数阶(循环实现)的计算操作数量 =", count)
|
||||
print("对数阶(循环实现)的计算操作数量 = \(count)")
|
||||
count = logRecur(n: n)
|
||||
print("对数阶(递归实现)的计算操作数量 =", count)
|
||||
print("对数阶(递归实现)的计算操作数量 = \(count)")
|
||||
|
||||
count = linearLogRecur(n: Double(n))
|
||||
print("线性对数阶(递归实现)的计算操作数量 =", count)
|
||||
print("线性对数阶(递归实现)的计算操作数量 = \(count)")
|
||||
|
||||
count = factorialRecur(n: n)
|
||||
print("阶乘阶(递归实现)的计算操作数量 =", count)
|
||||
print("阶乘阶(递归实现)的计算操作数量 = \(count)")
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱
|
||||
/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */
|
||||
func randomNumbers(n: Int) -> [Int] {
|
||||
// 生成数组 nums = { 1, 2, 3, ..., n }
|
||||
var nums = Array(1 ... n)
|
||||
@ -13,7 +13,7 @@ func randomNumbers(n: Int) -> [Int] {
|
||||
return nums
|
||||
}
|
||||
|
||||
// 查找数组 nums 中数字 1 所在索引
|
||||
/* 查找数组 nums 中数字 1 所在索引 */
|
||||
func findOne(nums: [Int]) -> Int {
|
||||
for i in nums.indices {
|
||||
if nums[i] == 1 {
|
||||
@ -25,14 +25,14 @@ func findOne(nums: [Int]) -> Int {
|
||||
|
||||
@main
|
||||
enum WorstBestTimeComplexity {
|
||||
// Driver Code
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
for _ in 0 ..< 10 {
|
||||
let n = 100
|
||||
let nums = randomNumbers(n: n)
|
||||
let index = findOne(nums: nums)
|
||||
print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums)
|
||||
print("数字 1 的索引为", index)
|
||||
print("数组 [ 1, 2, ..., n ] 被打乱后 = \(nums)")
|
||||
print("数字 1 的索引为 \(index)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
codes/swift/chapter_stack_and_queue/array_stack.swift
Normal file
84
codes/swift/chapter_stack_and_queue/array_stack.swift
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* File: array_stack.swift
|
||||
* Created Time: 2023-01-09
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 基于数组实现的栈 */
|
||||
class ArrayStack {
|
||||
private var stack: [Int]
|
||||
|
||||
init() {
|
||||
// 初始化列表(动态数组)
|
||||
stack = []
|
||||
}
|
||||
|
||||
/* 获取栈的长度 */
|
||||
func size() -> Int {
|
||||
stack.count
|
||||
}
|
||||
|
||||
/* 判断栈是否为空 */
|
||||
func isEmpty() -> Bool {
|
||||
stack.isEmpty
|
||||
}
|
||||
|
||||
/* 入栈 */
|
||||
func push(num: Int) {
|
||||
stack.append(num)
|
||||
}
|
||||
|
||||
/* 出栈 */
|
||||
func pop() -> Int {
|
||||
if stack.isEmpty {
|
||||
fatalError("栈为空")
|
||||
}
|
||||
return stack.removeLast()
|
||||
}
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
func peek() -> Int {
|
||||
if stack.isEmpty {
|
||||
fatalError("栈为空")
|
||||
}
|
||||
return stack.last!
|
||||
}
|
||||
|
||||
/* 将 List 转化为 Array 并返回 */
|
||||
func toArray() -> [Int] {
|
||||
stack
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _ArrayStack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 初始化栈 */
|
||||
let stack = ArrayStack()
|
||||
|
||||
/* 元素入栈 */
|
||||
stack.push(num: 1)
|
||||
stack.push(num: 3)
|
||||
stack.push(num: 2)
|
||||
stack.push(num: 5)
|
||||
stack.push(num: 4)
|
||||
print("栈 stack = \(stack.toArray())")
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
let peek = stack.peek()
|
||||
print("栈顶元素 peek = \(peek)")
|
||||
|
||||
/* 元素出栈 */
|
||||
let pop = stack.pop()
|
||||
print("出栈元素 pop = \(pop),出栈后 stack = \(stack.toArray())")
|
||||
|
||||
/* 获取栈的长度 */
|
||||
let size = stack.size()
|
||||
print("栈的长度 size = \(size)")
|
||||
|
||||
/* 判断是否为空 */
|
||||
let isEmpty = stack.isEmpty()
|
||||
print("栈是否为空 = \(isEmpty)")
|
||||
}
|
||||
}
|
93
codes/swift/chapter_stack_and_queue/linkedlist_stack.swift
Normal file
93
codes/swift/chapter_stack_and_queue/linkedlist_stack.swift
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* File: linkedlist_stack.swift
|
||||
* Created Time: 2023-01-09
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 基于链表实现的栈 */
|
||||
class LinkedListStack {
|
||||
private var _peek: ListNode? // 将头结点作为栈顶
|
||||
private var _size = 0 // 栈的长度
|
||||
|
||||
init() {}
|
||||
|
||||
/* 获取栈的长度 */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* 判断栈是否为空 */
|
||||
func isEmpty() -> Bool {
|
||||
_size == 0
|
||||
}
|
||||
|
||||
/* 入栈 */
|
||||
func push(num: Int) {
|
||||
let node = ListNode(x: num)
|
||||
node.next = _peek
|
||||
_peek = node
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 出栈 */
|
||||
func pop() -> Int {
|
||||
let num = peek()
|
||||
_peek = _peek?.next
|
||||
_size -= 1
|
||||
return num
|
||||
}
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
func peek() -> Int {
|
||||
if _size == 0 {
|
||||
fatalError("栈为空")
|
||||
}
|
||||
return _peek!.val
|
||||
}
|
||||
|
||||
/* 将 List 转化为 Array 并返回 */
|
||||
func toArray() -> [Int] {
|
||||
var node = _peek
|
||||
var res = Array(repeating: 0, count: _size)
|
||||
for i in sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) {
|
||||
res[i] = node!.val
|
||||
node = node?.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _LinkedListStack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 初始化栈 */
|
||||
let stack = LinkedListStack()
|
||||
|
||||
/* 元素入栈 */
|
||||
stack.push(num: 1)
|
||||
stack.push(num: 3)
|
||||
stack.push(num: 2)
|
||||
stack.push(num: 5)
|
||||
stack.push(num: 4)
|
||||
print("栈 stack = \(stack.toArray())")
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
let peek = stack.peek()
|
||||
print("栈顶元素 peek = \(peek)")
|
||||
|
||||
/* 元素出栈 */
|
||||
let pop = stack.pop()
|
||||
print("出栈元素 pop = \(pop),出栈后 stack = \(stack.toArray())")
|
||||
|
||||
/* 获取栈的长度 */
|
||||
let size = stack.size()
|
||||
print("栈的长度 size = \(size)")
|
||||
|
||||
/* 判断是否为空 */
|
||||
let isEmpty = stack.isEmpty()
|
||||
print("栈是否为空 = \(isEmpty)")
|
||||
}
|
||||
}
|
39
codes/swift/chapter_stack_and_queue/stack.swift
Normal file
39
codes/swift/chapter_stack_and_queue/stack.swift
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: stack.swift
|
||||
* Created Time: 2023-01-09
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
@main
|
||||
enum Stack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 初始化栈 */
|
||||
// Swift 没有内置的栈类,可以把 Array 当作栈来使用
|
||||
var stack: [Int] = []
|
||||
|
||||
/* 元素入栈 */
|
||||
stack.append(1)
|
||||
stack.append(3)
|
||||
stack.append(2)
|
||||
stack.append(5)
|
||||
stack.append(4)
|
||||
print("栈 stack = \(stack)")
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
let peek = stack.last!
|
||||
print("栈顶元素 peek = \(peek)")
|
||||
|
||||
/* 元素出栈 */
|
||||
let pop = stack.removeLast()
|
||||
print("出栈元素 pop = \(pop),出栈后 stack = \(stack)")
|
||||
|
||||
/* 获取栈的长度 */
|
||||
let size = stack.count
|
||||
print("栈的长度 size = \(size)")
|
||||
|
||||
/* 判断是否为空 */
|
||||
let isEmpty = stack.isEmpty
|
||||
print("栈是否为空 = \(isEmpty)")
|
||||
}
|
||||
}
|
@ -15,6 +15,16 @@ public enum PrintUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static func printLinkedList(head: ListNode) {
|
||||
var head: ListNode? = head
|
||||
var list: [String] = []
|
||||
while head != nil {
|
||||
list.append("\(head!.val)")
|
||||
head = head?.next
|
||||
}
|
||||
print(list.joined(separator: " -> "))
|
||||
}
|
||||
|
||||
public static func printTree(root: TreeNode?) {
|
||||
printTree(root: root, prev: nil, isLeft: false)
|
||||
}
|
||||
|
51
codes/typescript/chapter_searching/hashing_search.ts
Normal file
51
codes/typescript/chapter_searching/hashing_search.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: hashing_search.js
|
||||
* Created Time: 2022-12-29
|
||||
* Author: Zhuo Qinyue (1403450829@qq.com)
|
||||
*/
|
||||
|
||||
import { printLinkedList } from "../module/PrintUtil";
|
||||
import ListNode from "../module/ListNode";
|
||||
|
||||
|
||||
/* 哈希查找(数组) */
|
||||
function hashingSearch(map: Map<number, number>, target: number): number {
|
||||
// 哈希表的 key: 目标元素,value: 索引
|
||||
// 若哈希表中无此 key ,返回 -1
|
||||
return map.has(target) ? map.get(target) as number : -1;
|
||||
}
|
||||
|
||||
/* 哈希查找(链表) */
|
||||
function hashingSearch1(map: Map<number, ListNode>, target: number): ListNode | null {
|
||||
// 哈希表的 key: 目标结点值,value: 结点对象
|
||||
// 若哈希表中无此 key ,返回 null
|
||||
return map.has(target) ? map.get(target) as ListNode : null;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const target = 3;
|
||||
|
||||
/* 哈希查找(数组) */
|
||||
const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8];
|
||||
// 初始化哈希表
|
||||
const map = new Map();
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
map.set(nums[i], i); // key: 元素,value: 索引
|
||||
}
|
||||
const index = hashingSearch(map, target);
|
||||
console.log("目标元素 3 的索引 = " + index);
|
||||
|
||||
/* 哈希查找(链表) */
|
||||
let head = new ListNode().arrToLinkedList(nums)
|
||||
// 初始化哈希表
|
||||
const map1 = new Map();
|
||||
while (head != null) {
|
||||
map1.set(head.val, head); // key: 结点值,value: 结点
|
||||
head = head.next;
|
||||
}
|
||||
const node = hashingSearch1(map1, target);
|
||||
console.log("目标结点值 3 的对应结点对象为");
|
||||
printLinkedList(node);
|
||||
}
|
||||
|
||||
main();
|
47
codes/typescript/chapter_searching/linear_search.ts
Normal file
47
codes/typescript/chapter_searching/linear_search.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* File: linear_search.ts
|
||||
* Created Time: 2023-01-07
|
||||
* Author: Daniel (better.sunjian@gmail.com)
|
||||
*/
|
||||
|
||||
import ListNode from '../module/ListNode.ts';
|
||||
|
||||
/* 线性查找(数组)*/
|
||||
function linearSearchArray(nums: number[], target: number): number {
|
||||
// 遍历数组
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
// 找到目标元素,返回其索引
|
||||
if (nums[i] === target) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// 未找到目标元素,返回 -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 线性查找(链表)*/
|
||||
function linearSearchLinkedList(head: ListNode | null, target: number): ListNode | null {
|
||||
// 遍历链表
|
||||
while (head) {
|
||||
// 找到目标结点,返回之
|
||||
if (head.val === target) {
|
||||
return head;
|
||||
}
|
||||
head = head.next;
|
||||
}
|
||||
// 未找到目标结点,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
const target = 3;
|
||||
|
||||
/* 在数组中执行线性查找 */
|
||||
const nums = [ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 ];
|
||||
const index = linearSearchArray(nums, target);
|
||||
console.log('目标元素 3 的索引 =', index);
|
||||
|
||||
/* 在链表中执行线性查找 */
|
||||
const head = ListNode.arrToLinkedList(nums);
|
||||
const node = linearSearchLinkedList(head, target);
|
||||
console.log('目标结点值 3 的对应结点对象为', node);
|
@ -150,7 +150,7 @@ console.log('\n初始化的二叉树为\n');
|
||||
printTree(getRoot());
|
||||
|
||||
/* 查找结点 */
|
||||
let node = search(5);
|
||||
let node = search(7);
|
||||
console.log('\n查找到的结点对象为 ' + node + ',结点值 = ' + node!.val);
|
||||
|
||||
/* 插入结点 */
|
||||
|
@ -14,4 +14,19 @@ export default class ListNode {
|
||||
this.val = val === undefined ? 0 : val;
|
||||
this.next = next === undefined ? null : next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a linked list with an array
|
||||
* @param arr
|
||||
* @return
|
||||
*/
|
||||
arrToLinkedList(arr: number[]): ListNode | null {
|
||||
const dum: ListNode = new ListNode(0);
|
||||
let head = dum;
|
||||
for (const val of arr) {
|
||||
head.next = new ListNode(val);
|
||||
head = head.next;
|
||||
}
|
||||
return dum.next;
|
||||
}
|
||||
}
|
||||
|
2
codes/zig/.gitignore
vendored
Normal file
2
codes/zig/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
zig-cache/
|
||||
zig-out/
|
67
codes/zig/build.zig
Normal file
67
codes/zig/build.zig
Normal file
@ -0,0 +1,67 @@
|
||||
// File: build.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// Zig Version: 0.10.0
|
||||
// Build Command: zig build
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
// Section: "Time Complexity"
|
||||
// Source File: "chapter_computational_complexity/time_complexity.zig"
|
||||
// Run Command: zig build run_time_complexity
|
||||
const exe_time_complexity = b.addExecutable("time_complexity", "chapter_computational_complexity/time_complexity.zig");
|
||||
exe_time_complexity.addPackagePath("include", "include/include.zig");
|
||||
exe_time_complexity.setTarget(target);
|
||||
exe_time_complexity.setBuildMode(mode);
|
||||
exe_time_complexity.install();
|
||||
const run_cmd_time_complexity = exe_time_complexity.run();
|
||||
run_cmd_time_complexity.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd_time_complexity.addArgs(args);
|
||||
const run_step_time_complexity = b.step("run_time_complexity", "Run time_complexity");
|
||||
run_step_time_complexity.dependOn(&run_cmd_time_complexity.step);
|
||||
|
||||
// Source File: "chapter_computational_complexity/worst_best_time_complexity.zig"
|
||||
// Run Command: zig build run_worst_best_time_complexity
|
||||
const exe_worst_best_time_complexity = b.addExecutable("worst_best_time_complexity", "chapter_computational_complexity/worst_best_time_complexity.zig");
|
||||
exe_worst_best_time_complexity.addPackagePath("include", "include/include.zig");
|
||||
exe_worst_best_time_complexity.setTarget(target);
|
||||
exe_worst_best_time_complexity.setBuildMode(mode);
|
||||
exe_worst_best_time_complexity.install();
|
||||
const run_cmd_worst_best_time_complexity = exe_worst_best_time_complexity.run();
|
||||
run_cmd_worst_best_time_complexity.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd_worst_best_time_complexity.addArgs(args);
|
||||
const run_step_worst_best_time_complexity = b.step("run_worst_best_time_complexity", "Run worst_best_time_complexity");
|
||||
run_step_worst_best_time_complexity.dependOn(&run_cmd_worst_best_time_complexity.step);
|
||||
|
||||
// Section: "Space Complexity"
|
||||
// Source File: "chapter_computational_complexity/space_complexity.zig"
|
||||
// Run Command: zig build run_space_complexity
|
||||
const exe_space_complexity = b.addExecutable("space_complexity", "chapter_computational_complexity/space_complexity.zig");
|
||||
exe_space_complexity.addPackagePath("include", "include/include.zig");
|
||||
exe_space_complexity.setTarget(target);
|
||||
exe_space_complexity.setBuildMode(mode);
|
||||
exe_space_complexity.install();
|
||||
const run_cmd_space_complexity = exe_space_complexity.run();
|
||||
run_cmd_space_complexity.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd_space_complexity.addArgs(args);
|
||||
const run_step_space_complexity = b.step("run_space_complexity", "Run space_complexity");
|
||||
run_step_space_complexity.dependOn(&run_cmd_space_complexity.step);
|
||||
|
||||
// Section: "Space Time Tradeoff"
|
||||
// Source File: "chapter_computational_complexity/leetcode_two_sum.zig"
|
||||
// Run Command: zig build run_leetcode_two_sum
|
||||
const exe_leetcode_two_sum = b.addExecutable("leetcode_two_sum", "chapter_computational_complexity/leetcode_two_sum.zig");
|
||||
exe_leetcode_two_sum.addPackagePath("include", "include/include.zig");
|
||||
exe_leetcode_two_sum.setTarget(target);
|
||||
exe_leetcode_two_sum.setBuildMode(mode);
|
||||
exe_leetcode_two_sum.install();
|
||||
const run_cmd_leetcode_two_sum = exe_leetcode_two_sum.run();
|
||||
run_cmd_leetcode_two_sum.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd_leetcode_two_sum.addArgs(args);
|
||||
const run_step_leetcode_two_sum = b.step("run_leetcode_two_sum", "Run leetcode_two_sum");
|
||||
run_step_leetcode_two_sum.dependOn(&run_cmd_leetcode_two_sum.step);
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// File: leetcode_two_sum.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
const SolutionBruteForce = struct {
|
||||
pub fn twoSum(self: *SolutionBruteForce, nums: []i32, target: i32) [2]i32 {
|
||||
_ = self;
|
||||
var size: usize = nums.len;
|
||||
var i: usize = 0;
|
||||
// 两层循环,时间复杂度 O(n^2)
|
||||
while (i < size - 1) : (i += 1) {
|
||||
var j = i + 1;
|
||||
while (j < size) : (j += 1) {
|
||||
if (nums[i] + nums[j] == target) {
|
||||
return [_]i32{@intCast(i32, i), @intCast(i32, j)};
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const SolutionHashMap = struct {
|
||||
pub fn twoSum(self: *SolutionHashMap, nums: []i32, target: i32) ![2]i32 {
|
||||
_ = self;
|
||||
var size: usize = nums.len;
|
||||
// 辅助哈希表,空间复杂度 O(n)
|
||||
var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator);
|
||||
defer dic.deinit();
|
||||
var i: usize = 0;
|
||||
// 单层循环,时间复杂度 O(n)
|
||||
while (i < size) : (i += 1) {
|
||||
if (dic.contains(target - nums[i])) {
|
||||
return [_]i32{dic.get(target - nums[i]).?, @intCast(i32, i)};
|
||||
}
|
||||
try dic.put(nums[i], @intCast(i32, i));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// ======= Test Case =======
|
||||
var nums = [_]i32{ 2, 7, 11, 15 };
|
||||
var target: i32 = 9;
|
||||
// 方法一
|
||||
var slt1 = SolutionBruteForce{};
|
||||
var res = slt1.twoSum(&nums, target);
|
||||
std.debug.print("方法一 res = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &res);
|
||||
// 方法二
|
||||
var slt2 = SolutionHashMap{};
|
||||
res = try slt2.twoSum(&nums, target);
|
||||
std.debug.print("方法二 res = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &res);
|
||||
}
|
||||
|
125
codes/zig/chapter_computational_complexity/space_complexity.zig
Normal file
125
codes/zig/chapter_computational_complexity/space_complexity.zig
Normal file
@ -0,0 +1,125 @@
|
||||
// File: space_complexity.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 函数
|
||||
fn function() i32 {
|
||||
// do something
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 常数阶
|
||||
fn constant(n: i32) void {
|
||||
// 常量、变量、对象占用 O(1) 空间
|
||||
const a: i32 = 0;
|
||||
var b: i32 = 0;
|
||||
var nums = [_]i32{0}**10000;
|
||||
var node = inc.ListNode(i32){.val = 0};
|
||||
var i: i32 = 0;
|
||||
// 循环中的变量占用 O(1) 空间
|
||||
while (i < n) : (i += 1) {
|
||||
var c: i32 = 0;
|
||||
_ = c;
|
||||
}
|
||||
// 循环中的函数占用 O(1) 空间
|
||||
i = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
_ = function();
|
||||
}
|
||||
_ = a;
|
||||
_ = b;
|
||||
_ = nums;
|
||||
_ = node;
|
||||
}
|
||||
|
||||
// 线性阶
|
||||
fn linear(comptime n: i32) !void {
|
||||
// 长度为 n 的数组占用 O(n) 空间
|
||||
var nums = [_]i32{0}**n;
|
||||
// 长度为 n 的列表占用 O(n) 空间
|
||||
var nodes = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer nodes.deinit();
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
try nodes.append(i);
|
||||
}
|
||||
// 长度为 n 的哈希表占用 O(n) 空间
|
||||
var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator);
|
||||
defer map.deinit();
|
||||
var j: i32 = 0;
|
||||
while (j < n) : (j += 1) {
|
||||
const string = try std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{j});
|
||||
defer std.heap.page_allocator.free(string);
|
||||
try map.put(i, string);
|
||||
}
|
||||
_ = nums;
|
||||
}
|
||||
|
||||
// 线性阶(递归实现)
|
||||
fn linearRecur(comptime n: i32) void {
|
||||
std.debug.print("递归 n = {}\n", .{n});
|
||||
if (n == 1) return;
|
||||
linearRecur(n - 1);
|
||||
}
|
||||
|
||||
// 平方阶
|
||||
fn quadratic(n: i32) !void {
|
||||
// 二维列表占用 O(n^2) 空间
|
||||
var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator);
|
||||
defer nodes.deinit();
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
var tmp = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer tmp.deinit();
|
||||
var j: i32 = 0;
|
||||
while (j < n) : (j += 1) {
|
||||
try tmp.append(0);
|
||||
}
|
||||
try nodes.append(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
// 平方阶(递归实现)
|
||||
fn quadraticRecur(comptime n: i32) i32 {
|
||||
if (n <= 0) return 0;
|
||||
var nums = [_]i32{0}**n;
|
||||
std.debug.print("递归 n = {} 中的 nums 长度 = {}\n", .{n, nums.len});
|
||||
return quadraticRecur(n - 1);
|
||||
}
|
||||
|
||||
// 指数阶(建立满二叉树)
|
||||
fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) {
|
||||
if (n == 0) return null;
|
||||
const root = try mem_allocator.create(inc.TreeNode(i32));
|
||||
root.init(0);
|
||||
root.left = try buildTree(mem_allocator, n - 1);
|
||||
root.right = try buildTree(mem_allocator, n - 1);
|
||||
return root;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
const n: i32 = 5;
|
||||
// 常数阶
|
||||
constant(n);
|
||||
// 线性阶
|
||||
try linear(n);
|
||||
linearRecur(n);
|
||||
// 平方阶
|
||||
try quadratic(n);
|
||||
_ = quadraticRecur(n);
|
||||
// 指数阶
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
var root = blk_root: {
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
break :blk_root try buildTree(mem_allocator, n);
|
||||
};
|
||||
try inc.PrintUtil.printTree(root, null, false);
|
||||
|
||||
const getchar = try std.io.getStdIn().reader().readByte();
|
||||
_ = getchar;
|
||||
}
|
@ -59,6 +59,7 @@ fn bubbleSort(nums: []i32) i32 {
|
||||
var j: usize = 0;
|
||||
// 内循环:冒泡操作
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
var tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
@ -66,6 +67,7 @@ fn bubbleSort(nums: []i32) i32 {
|
||||
count += 3; // 元素交换包含 3 个单元操作
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -138,7 +140,7 @@ fn factorialRecur(n: i32) i32 {
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() void {
|
||||
pub fn main() !void {
|
||||
// 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势
|
||||
const n: i32 = 8;
|
||||
std.debug.print("输入数据大小 n = {}\n", .{n});
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱
|
||||
pub fn randomNumbers(comptime n: usize) [n]i32 {
|
||||
@ -33,10 +34,8 @@ pub fn main() !void {
|
||||
var nums = randomNumbers(n);
|
||||
var index = findOne(&nums);
|
||||
std.debug.print("\n数组 [ 1, 2, ..., n ] 被打乱后 = ", .{});
|
||||
for (nums) |num, j| {
|
||||
std.debug.print("{}{s}", .{num, if (j == nums.len-1) "" else "," });
|
||||
}
|
||||
std.debug.print("\n数字 1 的索引为 {}\n", .{index});
|
||||
inc.PrintUtil.printArray(i32, &nums);
|
||||
std.debug.print("数字 1 的索引为 {}\n", .{index});
|
||||
}
|
||||
}
|
||||
|
||||
|
21
codes/zig/include/ListNode.zig
Normal file
21
codes/zig/include/ListNode.zig
Normal file
@ -0,0 +1,21 @@
|
||||
// File: ListNode.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// Definition for a singly-linked list node
|
||||
// 编译期泛型
|
||||
pub fn ListNode(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
val: T = 0,
|
||||
next: ?*Self = null,
|
||||
|
||||
// Initialize a list node with specific value
|
||||
pub fn init(self: *Self, x: i32) void {
|
||||
self.val = x;
|
||||
}
|
||||
};
|
||||
}
|
73
codes/zig/include/PrintUtil.zig
Normal file
73
codes/zig/include/PrintUtil.zig
Normal file
@ -0,0 +1,73 @@
|
||||
// File: PrintUtil.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const ListNode = @import("ListNode.zig").ListNode;
|
||||
const TreeNode = @import("TreeNode.zig").TreeNode;
|
||||
|
||||
// Print an array
|
||||
// 编译期泛型
|
||||
pub fn printArray(comptime T: type, nums: []T) void {
|
||||
std.debug.print("[", .{});
|
||||
if (nums.len > 0) {
|
||||
for (nums) |num, j| {
|
||||
std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]\n" else ", " });
|
||||
}
|
||||
} else {
|
||||
std.debug.print("]", .{});
|
||||
std.debug.print("\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// This tree printer is borrowed from TECHIE DELIGHT
|
||||
// https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
const Trunk = struct {
|
||||
prev: ?*Trunk = null,
|
||||
str: []const u8 = undefined,
|
||||
|
||||
pub fn init(self: *Trunk, prev: ?*Trunk, str: []const u8) void {
|
||||
self.prev = prev;
|
||||
self.str = str;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to print branches of the binary tree
|
||||
pub fn showTrunks(p: ?*Trunk) void {
|
||||
if (p == null) return;
|
||||
showTrunks(p.?.prev);
|
||||
std.debug.print("{s}", .{p.?.str});
|
||||
}
|
||||
|
||||
// The interface of the tree printer
|
||||
// Print a binary tree
|
||||
pub fn printTree(root: ?*TreeNode(i32), prev: ?*Trunk, isLeft: bool) !void {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var prev_str = " ";
|
||||
var trunk = Trunk{.prev = prev, .str = prev_str};
|
||||
|
||||
try printTree(root.?.right, &trunk, true);
|
||||
|
||||
if (prev == null) {
|
||||
trunk.str = "———";
|
||||
} else if (isLeft) {
|
||||
trunk.str = "/———";
|
||||
prev_str = " |";
|
||||
} else {
|
||||
trunk.str = "\\———";
|
||||
prev.?.str = prev_str;
|
||||
}
|
||||
|
||||
showTrunks(&trunk);
|
||||
std.debug.print(" {}\n", .{root.?.val});
|
||||
|
||||
if (prev) |_| {
|
||||
prev.?.str = prev_str;
|
||||
}
|
||||
trunk.str = " |";
|
||||
|
||||
try printTree(root.?.left, &trunk, false);
|
||||
}
|
22
codes/zig/include/TreeNode.zig
Normal file
22
codes/zig/include/TreeNode.zig
Normal file
@ -0,0 +1,22 @@
|
||||
// File: TreeNode.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// Definition for a binary tree node
|
||||
// 编译期泛型
|
||||
pub fn TreeNode(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
val: T = undefined,
|
||||
left: ?*Self = null,
|
||||
right: ?*Self = null,
|
||||
|
||||
// Initialize a tree node with specific value
|
||||
pub fn init(self: *Self, x: i32) void {
|
||||
self.val = x;
|
||||
}
|
||||
};
|
||||
}
|
7
codes/zig/include/include.zig
Normal file
7
codes/zig/include/include.zig
Normal file
@ -0,0 +1,7 @@
|
||||
// File: include.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: sjinzh (sjinzh@gmail.com)
|
||||
|
||||
pub const PrintUtil = @import("PrintUtil.zig");
|
||||
pub const ListNode = @import("ListNode.zig").ListNode;
|
||||
pub const TreeNode = @import("TreeNode.zig").TreeNode;
|
@ -84,7 +84,7 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="array.swift"
|
||||
// 初始化数组
|
||||
/* 初始化数组 */
|
||||
let arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]
|
||||
let nums = [1, 3, 2, 5, 4]
|
||||
```
|
||||
@ -204,7 +204,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
=== "Swift"
|
||||
|
||||
```swift title="array.swift"
|
||||
// 随机返回一个数组元素
|
||||
/* 随机返回一个数组元素 */
|
||||
func randomAccess(nums: [Int]) -> Int {
|
||||
// 在区间 [0, nums.count) 中随机抽取一个数字
|
||||
let randomIndex = nums.indices.randomElement()!
|
||||
@ -341,7 +341,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
=== "Swift"
|
||||
|
||||
```swift title="array.swift"
|
||||
// 扩展数组长度
|
||||
/* 扩展数组长度 */
|
||||
func extend(nums: [Int], enlarge: Int) -> [Int] {
|
||||
// 初始化一个扩展长度后的数组
|
||||
var res = Array(repeating: 0, count: nums.count + enlarge)
|
||||
@ -356,9 +356,9 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
|
||||
**数组中插入或删除元素效率低下**。假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点:
|
||||
|
||||
- **时间复杂度高:** 数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。
|
||||
- **丢失元素:** 由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会被丢失。
|
||||
- **内存浪费:** 我们一般会初始化一个比较长的数组,只用前面一部分,这样在插入数据时,丢失的末尾元素都是我们不关心的,但这样做同时也会造成内存空间的浪费。
|
||||
- **时间复杂度高**:数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。
|
||||
- **丢失元素**:由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会被丢失。
|
||||
- **内存浪费**:我们一般会初始化一个比较长的数组,只用前面一部分,这样在插入数据时,丢失的末尾元素都是我们不关心的,但这样做同时也会造成内存空间的浪费。
|
||||
|
||||
![array_insert_remove_element](array.assets/array_insert_remove_element.png)
|
||||
|
||||
@ -526,7 +526,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
=== "Swift"
|
||||
|
||||
```swift title="array.swift"
|
||||
// 在数组的索引 index 处插入元素 num
|
||||
/* 在数组的索引 index 处插入元素 num */
|
||||
func insert(nums: inout [Int], num: Int, index: Int) {
|
||||
// 把索引 index 以及之后的所有元素向后移动一位
|
||||
for i in sequence(first: nums.count - 1, next: { $0 > index + 1 ? $0 - 1 : nil }) {
|
||||
@ -536,7 +536,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
// 删除索引 index 处元素
|
||||
/* 删除索引 index 处元素 */
|
||||
func remove(nums: inout [Int], index: Int) {
|
||||
let count = nums.count
|
||||
// 把索引 index 之后的所有元素向前移动一位
|
||||
@ -674,7 +674,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
=== "Swift"
|
||||
|
||||
```swift title="array.swift"
|
||||
// 遍历数组
|
||||
/* 遍历数组 */
|
||||
func traverse(nums: [Int]) {
|
||||
var count = 0
|
||||
// 通过索引遍历数组
|
||||
@ -793,7 +793,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
=== "Swift"
|
||||
|
||||
```swift title="array.swift"
|
||||
// 在数组中查找指定元素
|
||||
/* 在数组中查找指定元素 */
|
||||
func find(nums: [Int], target: Int) -> Int {
|
||||
for i in nums.indices {
|
||||
if nums[i] == target {
|
||||
|
@ -115,7 +115,15 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title=""
|
||||
/* 链表结点类 */
|
||||
class ListNode {
|
||||
var val: Int // 结点值
|
||||
var next: ListNode? // 指向下一结点的指针(引用)
|
||||
|
||||
init(x: Int) { // 构造函数
|
||||
val = x
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**尾结点指向什么?** 我们一般将链表的最后一个结点称为「尾结点」,其指向的是「空」,在 Java / C++ / Python 中分别记为 `null` / `nullptr` / `None` 。在不引起歧义下,本书都使用 `null` 来表示空。
|
||||
@ -255,7 +263,18 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="linked_list.swift"
|
||||
|
||||
/* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */
|
||||
// 初始化各个结点
|
||||
let n0 = ListNode(x: 1)
|
||||
let n1 = ListNode(x: 3)
|
||||
let n2 = ListNode(x: 2)
|
||||
let n3 = ListNode(x: 5)
|
||||
let n4 = ListNode(x: 4)
|
||||
// 构建引用指向
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
```
|
||||
|
||||
## 链表优点
|
||||
@ -381,6 +400,7 @@ comments: true
|
||||
n0.next = P;
|
||||
P.next = n1;
|
||||
}
|
||||
|
||||
/* 删除链表的结点 n0 之后的首个结点 */
|
||||
function remove(n0: ListNode): void {
|
||||
if (!n0.next) {
|
||||
@ -425,7 +445,24 @@ 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
|
||||
}
|
||||
|
||||
/* 删除链表的结点 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
|
||||
}
|
||||
```
|
||||
|
||||
## 链表缺点
|
||||
@ -438,9 +475,9 @@ comments: true
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
ListNode access(ListNode head, int index) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
head = head.next;
|
||||
if (head == null)
|
||||
return null;
|
||||
head = head.next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
@ -452,9 +489,9 @@ comments: true
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
ListNode* access(ListNode* head, int index) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
head = head->next;
|
||||
if (head == nullptr)
|
||||
return nullptr;
|
||||
head = head->next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
@ -466,9 +503,9 @@ comments: true
|
||||
""" 访问链表中索引为 index 的结点 """
|
||||
def access(head, index):
|
||||
for _ in range(index):
|
||||
head = head.next
|
||||
if not head:
|
||||
return None
|
||||
head = head.next
|
||||
return head
|
||||
```
|
||||
|
||||
@ -478,10 +515,10 @@ comments: true
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
func access(head *ListNode, index int) *ListNode {
|
||||
for i := 0; i < index; i++ {
|
||||
head = head.Next
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
head = head.Next
|
||||
}
|
||||
return head
|
||||
}
|
||||
@ -530,9 +567,9 @@ comments: true
|
||||
{
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
head = head.next;
|
||||
if (head == null)
|
||||
return null;
|
||||
head = head.next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
@ -541,7 +578,17 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="linked_list.swift"
|
||||
|
||||
/* 访问链表中索引为 index 的结点 */
|
||||
func access(head: ListNode, index: Int) -> ListNode? {
|
||||
var head: ListNode? = head
|
||||
for _ in 0 ..< index {
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
head = head?.next
|
||||
}
|
||||
return head
|
||||
}
|
||||
```
|
||||
|
||||
**链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。
|
||||
@ -674,7 +721,19 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="linked_list.swift"
|
||||
|
||||
/* 在链表中查找值为 target 的首个结点 */
|
||||
func find(head: ListNode, target: Int) -> Int {
|
||||
var head: ListNode? = head
|
||||
var index = 0
|
||||
while head != nil {
|
||||
if head?.val == target {
|
||||
return index
|
||||
}
|
||||
head = head?.next
|
||||
index += 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
```
|
||||
|
||||
## 常见链表类型
|
||||
@ -793,7 +852,16 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title=""
|
||||
/* 双向链表结点类 */
|
||||
class ListNode {
|
||||
var val: Int // 结点值
|
||||
var next: ListNode? // 指向后继结点的指针(引用)
|
||||
var prev: ListNode? // 指向前驱结点的指针(引用)
|
||||
|
||||
init(x: Int) { // 构造函数
|
||||
val = x
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
![linkedlist_common_types](linked_list.assets/linkedlist_common_types.png)
|
||||
|
@ -94,7 +94,11 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="list.swift"
|
||||
|
||||
/* 初始化列表 */
|
||||
// 无初始值
|
||||
let list1: [Int] = []
|
||||
// 有初始值
|
||||
var list = [1, 3, 2, 5, 4]
|
||||
```
|
||||
|
||||
**访问与更新元素**。列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。
|
||||
@ -178,7 +182,11 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="list.swift"
|
||||
/* 访问元素 */
|
||||
let num = list[1] // 访问索引 1 处的元素
|
||||
|
||||
/* 更新元素 */
|
||||
list[1] = 0 // 将索引 1 处的元素更新为 0
|
||||
```
|
||||
|
||||
**在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。
|
||||
@ -332,7 +340,21 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="list.swift"
|
||||
/* 清空列表 */
|
||||
list.removeAll()
|
||||
|
||||
/* 尾部添加元素 */
|
||||
list.append(1)
|
||||
list.append(3)
|
||||
list.append(2)
|
||||
list.append(5)
|
||||
list.append(4)
|
||||
|
||||
/* 中间插入元素 */
|
||||
list.insert(6, at: 3) // 在索引 3 处插入数字 6
|
||||
|
||||
/* 删除元素 */
|
||||
list.remove(at: 3) // 删除索引 3 处的元素
|
||||
```
|
||||
|
||||
**遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。
|
||||
@ -458,7 +480,17 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="list.swift"
|
||||
/* 通过索引遍历列表 */
|
||||
var count = 0
|
||||
for _ in list.indices {
|
||||
count += 1
|
||||
}
|
||||
|
||||
/* 直接遍历列表元素 */
|
||||
count = 0
|
||||
for _ in list {
|
||||
count += 1
|
||||
}
|
||||
```
|
||||
|
||||
**拼接两个列表**。再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。
|
||||
@ -529,7 +561,9 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="list.swift"
|
||||
|
||||
/* 拼接两个列表 */
|
||||
let list1 = [6, 8, 7, 10, 9]
|
||||
list.append(contentsOf: list1) // 将列表 list1 拼接到 list 之后
|
||||
```
|
||||
|
||||
**排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。
|
||||
@ -592,16 +626,17 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="list.swift"
|
||||
|
||||
/* 排序列表 */
|
||||
list.sort() // 排序后,列表元素从小到大排列
|
||||
```
|
||||
|
||||
## 列表简易实现 *
|
||||
|
||||
为了帮助加深对列表的理解,我们在此提供一个列表的简易版本的实现。需要关注三个核心点:
|
||||
|
||||
- **初始容量:** 选取一个合理的数组的初始容量 `initialCapacity` 。在本示例中,我们选择 10 作为初始容量。
|
||||
- **数量记录:** 需要声明一个变量 `size` ,用来记录列表当前有多少个元素,并随着元素插入与删除实时更新。根据此变量,可以定位列表的尾部,以及判断是否需要扩容。
|
||||
- **扩容机制:** 插入元素有可能导致超出列表容量,此时需要扩容列表,方法是建立一个更大的数组来替换当前数组。需要给定一个扩容倍数 `extendRatio` ,在本示例中,我们规定每次将数组扩容至之前的 2 倍。
|
||||
- **初始容量**:选取一个合理的数组的初始容量 `initialCapacity` 。在本示例中,我们选择 10 作为初始容量。
|
||||
- **数量记录**:需要声明一个变量 `size` ,用来记录列表当前有多少个元素,并随着元素插入与删除实时更新。根据此变量,可以定位列表的尾部,以及判断是否需要扩容。
|
||||
- **扩容机制**:插入元素有可能导致超出列表容量,此时需要扩容列表,方法是建立一个更大的数组来替换当前数组。需要给定一个扩容倍数 `extendRatio` ,在本示例中,我们规定每次将数组扩容至之前的 2 倍。
|
||||
|
||||
本示例是为了帮助读者对如何实现列表产生直观的认识。实际编程语言中,列表的实现远比以下代码复杂且标准,感兴趣的读者可以查阅源码学习。
|
||||
|
||||
@ -864,7 +899,7 @@ comments: true
|
||||
|
||||
```go title="my_list.go"
|
||||
/* 列表类简易实现 */
|
||||
type MyList struct {
|
||||
type myList struct {
|
||||
numsCapacity int
|
||||
nums []int
|
||||
numsSize int
|
||||
@ -872,8 +907,8 @@ comments: true
|
||||
}
|
||||
|
||||
/* 构造函数 */
|
||||
func newMyList() *MyList {
|
||||
return &MyList{
|
||||
func newMyList() *myList {
|
||||
return &myList{
|
||||
numsCapacity: 10, // 列表容量
|
||||
nums: make([]int, 10), // 数组(存储列表元素)
|
||||
numsSize: 0, // 列表长度(即当前元素数量)
|
||||
@ -882,17 +917,17 @@ comments: true
|
||||
}
|
||||
|
||||
/* 获取列表长度(即当前元素数量) */
|
||||
func (l *MyList) size() int {
|
||||
func (l *myList) size() int {
|
||||
return l.numsSize
|
||||
}
|
||||
|
||||
/* 获取列表容量 */
|
||||
func (l *MyList) capacity() int {
|
||||
func (l *myList) capacity() int {
|
||||
return l.numsCapacity
|
||||
}
|
||||
|
||||
/* 访问元素 */
|
||||
func (l *MyList) get(index int) int {
|
||||
func (l *myList) get(index int) int {
|
||||
// 索引如果越界则抛出异常,下同
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
@ -901,7 +936,7 @@ comments: true
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
func (l *MyList) set(num, index int) {
|
||||
func (l *myList) set(num, index int) {
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
}
|
||||
@ -909,7 +944,7 @@ comments: true
|
||||
}
|
||||
|
||||
/* 尾部添加元素 */
|
||||
func (l *MyList) add(num int) {
|
||||
func (l *myList) add(num int) {
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if l.numsSize == l.numsCapacity {
|
||||
l.extendCapacity()
|
||||
@ -920,7 +955,7 @@ comments: true
|
||||
}
|
||||
|
||||
/* 中间插入元素 */
|
||||
func (l *MyList) insert(num, index int) {
|
||||
func (l *myList) insert(num, index int) {
|
||||
if index >= l.numsSize {
|
||||
panic("索引越界")
|
||||
}
|
||||
@ -938,20 +973,23 @@ comments: true
|
||||
}
|
||||
|
||||
/* 删除元素 */
|
||||
func (l *MyList) Remove(index int) {
|
||||
func (l *myList) remove(index int) int {
|
||||
if 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() {
|
||||
func (l *myList) extendCapacity() {
|
||||
// 新建一个长度为 self.__size 的数组,并将原数组拷贝到新数组
|
||||
l.nums = append(l.nums, make([]int, l.numsCapacity*(l.extendRatio-1))...)
|
||||
// 更新列表容量
|
||||
@ -1260,6 +1298,106 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="my_list.swift"
|
||||
/* 列表类简易实现 */
|
||||
class MyList {
|
||||
private var nums: [Int] // 数组(存储列表元素)
|
||||
private var _capacity = 10 // 列表容量
|
||||
private var _size = 0 // 列表长度(即当前元素数量)
|
||||
private let extendRatio = 2 // 每次列表扩容的倍数
|
||||
|
||||
/* 构造函数 */
|
||||
init() {
|
||||
nums = Array(repeating: 0, count: _capacity)
|
||||
}
|
||||
|
||||
/* 获取列表长度(即当前元素数量)*/
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* 获取列表容量 */
|
||||
func capacity() -> Int {
|
||||
_capacity
|
||||
}
|
||||
|
||||
/* 访问元素 */
|
||||
func get(index: Int) -> Int {
|
||||
// 索引如果越界则抛出错误,下同
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
return nums[index]
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
func set(index: Int, num: Int) {
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
/* 尾部添加元素 */
|
||||
func add(num: Int) {
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if _size == _capacity {
|
||||
extendCapacity()
|
||||
}
|
||||
nums[_size] = num
|
||||
// 更新元素数量
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 中间插入元素 */
|
||||
func insert(index: Int, num: Int) {
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if _size == _capacity {
|
||||
extendCapacity()
|
||||
}
|
||||
// 将索引 index 以及之后的元素都向后移动一位
|
||||
for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) {
|
||||
nums[j + 1] = nums[j]
|
||||
}
|
||||
nums[index] = num
|
||||
// 更新元素数量
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 删除元素 */
|
||||
@discardableResult
|
||||
func remove(index: Int) -> Int {
|
||||
if index >= _size {
|
||||
fatalError("索引越界")
|
||||
}
|
||||
let num = nums[index]
|
||||
// 将索引 index 之后的元素都向前移动一位
|
||||
for j in index ..< (_size - 1) {
|
||||
nums[j] = nums[j + 1]
|
||||
}
|
||||
// 更新元素数量
|
||||
_size -= 1
|
||||
// 返回被删除元素
|
||||
return num
|
||||
}
|
||||
|
||||
/* 列表扩容 */
|
||||
func extendCapacity() {
|
||||
// 新建一个长度为 size 的数组,并将原数组拷贝到新数组
|
||||
nums = nums + Array(repeating: 0, count: _capacity * (extendRatio - 1))
|
||||
// 更新列表容量
|
||||
_capacity = nums.count
|
||||
}
|
||||
|
||||
/* 将列表转换为数组 */
|
||||
func toArray() -> [Int] {
|
||||
var nums = Array(repeating: 0, count: _size)
|
||||
for i in 0 ..< _size {
|
||||
nums[i] = get(index: i)
|
||||
}
|
||||
return nums
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -103,14 +103,14 @@ comments: true
|
||||
|
||||
```go title=""
|
||||
/* 结构体 */
|
||||
type Node struct {
|
||||
type node struct {
|
||||
val int
|
||||
next *Node
|
||||
next *node
|
||||
}
|
||||
|
||||
/* 创建 Node 结构体 */
|
||||
func newNode(val int) *Node {
|
||||
return &Node{val: val}
|
||||
/* 创建 node 结构体 */
|
||||
func newNode(val int) *node {
|
||||
return &node{val: val}
|
||||
}
|
||||
|
||||
/* 函数 */
|
||||
@ -177,7 +177,7 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title=""
|
||||
// 类
|
||||
/* 类 */
|
||||
class Node {
|
||||
var val: Int
|
||||
var next: Node?
|
||||
@ -187,7 +187,7 @@ comments: true
|
||||
}
|
||||
}
|
||||
|
||||
// 函数
|
||||
/* 函数 */
|
||||
func function() -> Int {
|
||||
// do something...
|
||||
return 0
|
||||
@ -436,14 +436,14 @@ comments: true
|
||||
return 0
|
||||
}
|
||||
|
||||
// 循环 O(1)
|
||||
/* 循环 O(1) */
|
||||
func loop(n: Int) {
|
||||
for _ in 0 ..< n {
|
||||
function()
|
||||
}
|
||||
}
|
||||
|
||||
// 递归 O(n)
|
||||
/* 递归 O(n) */
|
||||
func recur(n: Int) {
|
||||
if n == 1 {
|
||||
return
|
||||
@ -604,7 +604,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="space_complexity.swift"
|
||||
// 常数阶
|
||||
/* 常数阶 */
|
||||
func constant(n: Int) {
|
||||
// 常量、变量、对象占用 O(1) 空间
|
||||
let a = 0
|
||||
@ -687,7 +687,7 @@ $$
|
||||
// 长度为 n 的数组占用 O(n) 空间
|
||||
_ = make([]int, n)
|
||||
// 长度为 n 的列表占用 O(n) 空间
|
||||
var nodes []*Node
|
||||
var nodes []*node
|
||||
for i := 0; i < n; i++ {
|
||||
nodes = append(nodes, newNode(i))
|
||||
}
|
||||
@ -743,7 +743,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="space_complexity.swift"
|
||||
// 线性阶
|
||||
/* 线性阶 */
|
||||
func linear(n: Int) {
|
||||
// 长度为 n 的数组占用 O(n) 空间
|
||||
let nums = Array(repeating: 0, count: n)
|
||||
@ -834,7 +834,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="space_complexity.swift"
|
||||
// 线性阶(递归实现)
|
||||
/* 线性阶(递归实现) */
|
||||
func linearRecur(n: Int) {
|
||||
print("递归 n = \(n)")
|
||||
if n == 1 {
|
||||
@ -954,7 +954,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="space_complexity.swift"
|
||||
// 平方阶
|
||||
/* 平方阶 */
|
||||
func quadratic(n: Int) {
|
||||
// 二维列表占用 O(n^2) 空间
|
||||
let numList = Array(repeating: Array(repeating: 0, count: n), count: n)
|
||||
@ -1047,7 +1047,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="space_complexity.swift"
|
||||
// 平方阶(递归实现)
|
||||
/* 平方阶(递归实现) */
|
||||
func quadraticRecur(n: Int) -> Int {
|
||||
if n <= 0 {
|
||||
return 0
|
||||
@ -1108,7 +1108,7 @@ $$
|
||||
|
||||
```go title="space_complexity.go"
|
||||
/* 指数阶(建立满二叉树) */
|
||||
func buildTree(n int) *TreeNode {
|
||||
func buildTree(n int) *treeNode {
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -1154,7 +1154,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="space_complexity.swift"
|
||||
// 指数阶(建立满二叉树)
|
||||
/* 指数阶(建立满二叉树) */
|
||||
func buildTree(n: Int) -> TreeNode? {
|
||||
if n == 0 {
|
||||
return nil
|
||||
|
@ -876,7 +876,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 常数阶
|
||||
/* 常数阶 */
|
||||
func constant(n: Int) -> Int {
|
||||
var count = 0
|
||||
let size = 100000
|
||||
@ -990,7 +990,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 线性阶
|
||||
/* 线性阶 */
|
||||
func linear(n: Int) -> Int {
|
||||
var count = 0
|
||||
for _ in 0 ..< n {
|
||||
@ -1121,7 +1121,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 线性阶(遍历数组)
|
||||
/* 线性阶(遍历数组) */
|
||||
func arrayTraversal(nums: [Int]) -> Int {
|
||||
var count = 0
|
||||
// 循环次数与数组长度成正比
|
||||
@ -1267,7 +1267,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 平方阶
|
||||
/* 平方阶 */
|
||||
func quadratic(n: Int) -> Int {
|
||||
var count = 0
|
||||
// 循环次数与数组长度成平方关系
|
||||
@ -1434,12 +1434,15 @@ $$
|
||||
for (int i = n - 1; i > 0; i--) {
|
||||
// 内循环:冒泡操作
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums [j + 1])
|
||||
{
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
int tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
count += 3; // 元素交换包含 3 个单元操作
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return count;
|
||||
@ -1477,11 +1480,11 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 平方阶(冒泡排序)
|
||||
/* 平方阶(冒泡排序) */
|
||||
func bubbleSort(nums: inout [Int]) -> Int {
|
||||
var count = 0 // 计数器
|
||||
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for i in sequence(first: nums.count - 1, next: { $0 > 0 ? $0 - 1 : nil }) {
|
||||
for i in sequence(first: nums.count - 1, next: { $0 > 0 + 1 ? $0 - 1 : nil }) {
|
||||
// 内循环:冒泡操作
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
@ -1656,7 +1659,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 指数阶(循环实现)
|
||||
/* 指数阶(循环实现) */
|
||||
func exponential(n: Int) -> Int {
|
||||
var count = 0
|
||||
var base = 1
|
||||
@ -1764,7 +1767,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 指数阶(递归实现)
|
||||
/* 指数阶(递归实现) */
|
||||
func expRecur(n: Int) -> Int {
|
||||
if n == 1 {
|
||||
return 1
|
||||
@ -1896,7 +1899,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 对数阶(循环实现)
|
||||
/* 对数阶(循环实现) */
|
||||
func logarithmic(n: Int) -> Int {
|
||||
var count = 0
|
||||
var n = n
|
||||
@ -1999,7 +2002,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 对数阶(递归实现)
|
||||
/* 对数阶(递归实现) */
|
||||
func logRecur(n: Int) -> Int {
|
||||
if n <= 1 {
|
||||
return 0
|
||||
@ -2137,7 +2140,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 线性对数阶
|
||||
/* 线性对数阶 */
|
||||
func linearLogRecur(n: Double) -> Int {
|
||||
if n <= 1 {
|
||||
return 1
|
||||
@ -2288,7 +2291,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="time_complexity.swift"
|
||||
// 阶乘阶(递归实现)
|
||||
/* 阶乘阶(递归实现) */
|
||||
func factorialRecur(n: Int) -> Int {
|
||||
if n == 0 {
|
||||
return 1
|
||||
@ -2658,7 +2661,7 @@ $$
|
||||
=== "Swift"
|
||||
|
||||
```swift title="worst_best_time_complexity.swift"
|
||||
// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱
|
||||
/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */
|
||||
func randomNumbers(n: Int) -> [Int] {
|
||||
// 生成数组 nums = { 1, 2, 3, ..., n }
|
||||
var nums = Array(1 ... n)
|
||||
@ -2667,7 +2670,7 @@ $$
|
||||
return nums
|
||||
}
|
||||
|
||||
// 查找数组 nums 中数字 1 所在索引
|
||||
/* 查找数组 nums 中数字 1 所在索引 */
|
||||
func findOne(nums: [Int]) -> Int {
|
||||
for i in nums.indices {
|
||||
if nums[i] == 1 {
|
||||
@ -2677,14 +2680,14 @@ $$
|
||||
return -1
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
/* Driver Code */
|
||||
func main() {
|
||||
for _ in 0 ..< 10 {
|
||||
let n = 100
|
||||
let nums = randomNumbers(n: n)
|
||||
let index = findOne(nums: nums)
|
||||
print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums)
|
||||
print("数字 1 的索引为", index)
|
||||
print("数组 [ 1, 2, ..., n ] 被打乱后 = \(nums)")
|
||||
print("数字 1 的索引为 \(index)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -12,8 +12,8 @@ comments: true
|
||||
|
||||
我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线形的(例如是网状或树状的),那么就是非线性数据结构。
|
||||
|
||||
- **线性数据结构:** 数组、链表、栈、队列、哈希表;
|
||||
- **非线性数据结构:** 树、图、堆、哈希表;
|
||||
- **线性数据结构**:数组、链表、栈、队列、哈希表;
|
||||
- **非线性数据结构**:树、图、堆、哈希表;
|
||||
|
||||
![classification_logic_structure](classification_of_data_structure.assets/classification_logic_structure.png)
|
||||
|
||||
@ -33,8 +33,8 @@ comments: true
|
||||
|
||||
**所有数据结构都是基于数组、或链表、或两者组合实现的**。例如栈和队列,既可以使用数组实现、也可以使用链表实现,而例如哈希表,其实现同时包含了数组和链表。
|
||||
|
||||
- **基于数组可实现:** 栈、队列、堆、哈希表、矩阵、张量(维度 $\geq 3$ 的数组)等;
|
||||
- **基于链表可实现:** 栈、队列、堆、哈希表、树、图等;
|
||||
- **基于数组可实现**:栈、队列、堆、哈希表、矩阵、张量(维度 $\geq 3$ 的数组)等;
|
||||
- **基于链表可实现**:栈、队列、堆、哈希表、树、图等;
|
||||
|
||||
基于数组实现的数据结构也被称为「静态数据结构」,这意味着该数据结构在在被初始化后,长度不可变。相反地,基于链表实现的数据结构被称为「动态数据结构」,该数据结构在被初始化后,我们也可以在程序运行中修改其长度。
|
||||
|
||||
|
@ -117,7 +117,7 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title=""
|
||||
// 使用多种「基本数据类型」来初始化「数组」
|
||||
/* 使用多种「基本数据类型」来初始化「数组」 */
|
||||
let numbers = Array(repeating: Int(), count: 5)
|
||||
let decimals = Array(repeating: Double(), count: 5)
|
||||
let characters = Array(repeating: Character("a"), count: 5)
|
||||
|
@ -24,9 +24,9 @@ comments: true
|
||||
|
||||
在原始哈希表中,一个桶地址只能存储一个元素(即键值对)。**考虑将桶地址内的单个元素转变成一个链表,将所有冲突元素都存储在一个链表中**,此时哈希表操作方法为:
|
||||
|
||||
- **查询元素:** 先将 key 输入到哈希函数得到桶地址(即访问链表头部),再遍历链表来确定对应的 value 。
|
||||
- **添加元素:** 先通过哈希函数访问链表头部,再将元素直接添加到链表头部即可。
|
||||
- **删除元素:** 同样先访问链表头部,再遍历链表查找对应元素,删除之即可。
|
||||
- **查询元素**:先将 key 输入到哈希函数得到桶地址(即访问链表头部),再遍历链表来确定对应的 value 。
|
||||
- **添加元素**:先通过哈希函数访问链表头部,再将元素直接添加到链表头部即可。
|
||||
- **删除元素**:同样先访问链表头部,再遍历链表查找对应元素,删除之即可。
|
||||
|
||||
(图)
|
||||
|
||||
@ -46,9 +46,9 @@ comments: true
|
||||
|
||||
「线性探测」使用固定步长的线性查找来解决哈希冲突。
|
||||
|
||||
**插入元素:** 如果出现哈希冲突,则从冲突位置向后线性遍历(步长一般取 1 ),直到找到一个空位,则将元素插入到该空位中。
|
||||
**插入元素**:如果出现哈希冲突,则从冲突位置向后线性遍历(步长一般取 1 ),直到找到一个空位,则将元素插入到该空位中。
|
||||
|
||||
**查找元素:** 若出现哈希冲突,则使用相同步长执行线性查找,会遇到两种情况:
|
||||
**查找元素**:若出现哈希冲突,则使用相同步长执行线性查找,会遇到两种情况:
|
||||
|
||||
1. 找到对应元素,返回 value 即可;
|
||||
2. 若遇到空位,则说明查找键值对不在哈希表中;
|
||||
@ -64,9 +64,9 @@ comments: true
|
||||
|
||||
顾名思义,「多次哈希」的思路是基于多个哈希函数 $f_1(x)$ , $f_2(x)$ , $f_3(x)$ , $\cdots$ 进行探测。
|
||||
|
||||
**插入元素:** 若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推……直到找到空位后插入元素。
|
||||
**插入元素**:若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推……直到找到空位后插入元素。
|
||||
|
||||
**查找元素:** 以相同的哈希函数顺序查找,存在两种情况:
|
||||
**查找元素**:以相同的哈希函数顺序查找,存在两种情况:
|
||||
|
||||
1. 找到目标元素,则返回之;
|
||||
2. 到空位或已尝试所有哈希函数,说明哈希表中无此元素;
|
||||
|
@ -16,10 +16,10 @@ comments: true
|
||||
|
||||
除了哈希表之外,还可以使用以下数据结构来实现上述查询功能:
|
||||
|
||||
1. **无序数组:** 每个元素为 `[学号, 姓名]` ;
|
||||
2. **有序数组:** 将 `1.` 中的数组按照学号从小到大排序;
|
||||
3. **链表:** 每个结点的值为 `[学号, 姓名]` ;
|
||||
4. **二叉搜索树:** 每个结点的值为 `[学号, 姓名]` ,根据学号大小来构建树;
|
||||
1. **无序数组**:每个元素为 `[学号, 姓名]` ;
|
||||
2. **有序数组**:将 `1.` 中的数组按照学号从小到大排序;
|
||||
3. **链表**:每个结点的值为 `[学号, 姓名]` ;
|
||||
4. **二叉搜索树**:每个结点的值为 `[学号, 姓名]` ,根据学号大小来构建树;
|
||||
|
||||
使用上述方法,各项操作的时间复杂度如下表所示(在此不做赘述,详解可见 [二叉搜索树章节](https://www.hello-algo.com/chapter_tree/binary_search_tree/#_6))。无论是查找元素、还是增删元素,哈希表的时间复杂度都是 $O(1)$ ,全面胜出!
|
||||
|
||||
@ -524,30 +524,30 @@ $$
|
||||
|
||||
```go title="array_hash_map.go"
|
||||
/* 键值对 int->String */
|
||||
type Entry struct {
|
||||
type entry struct {
|
||||
key int
|
||||
val string
|
||||
}
|
||||
|
||||
/* 基于数组简易实现的哈希表 */
|
||||
type ArrayHashMap struct {
|
||||
bucket []*Entry
|
||||
type arrayHashMap struct {
|
||||
bucket []*entry
|
||||
}
|
||||
|
||||
func newArrayHashMap() *ArrayHashMap {
|
||||
func newArrayHashMap() *arrayHashMap {
|
||||
// 初始化一个长度为 100 的桶(数组)
|
||||
bucket := make([]*Entry, 100)
|
||||
return &ArrayHashMap{bucket: bucket}
|
||||
bucket := make([]*entry, 100)
|
||||
return &arrayHashMap{bucket: bucket}
|
||||
}
|
||||
|
||||
/* 哈希函数 */
|
||||
func (a *ArrayHashMap) hashFunc(key int) int {
|
||||
func (a *arrayHashMap) hashFunc(key int) int {
|
||||
index := key % 100
|
||||
return index
|
||||
}
|
||||
|
||||
/* 查询操作 */
|
||||
func (a *ArrayHashMap) get(key int) string {
|
||||
func (a *arrayHashMap) get(key int) string {
|
||||
index := a.hashFunc(key)
|
||||
pair := a.bucket[index]
|
||||
if pair == nil {
|
||||
@ -557,16 +557,16 @@ $$
|
||||
}
|
||||
|
||||
/* 添加操作 */
|
||||
func (a *ArrayHashMap) put(key int, val string) {
|
||||
pair := &Entry{key: key, val: 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) {
|
||||
func (a *arrayHashMap) remove(key int) {
|
||||
index := a.hashFunc(key)
|
||||
// 置为空字符,代表删除
|
||||
// 置为 nil ,代表删除
|
||||
a.bucket[index] = nil
|
||||
}
|
||||
```
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user