(PR #204) update c code and doc for time_complexity

This commit is contained in:
sjinzh
2023-01-05 08:39:09 +08:00
23 changed files with 447 additions and 172 deletions

View File

@@ -4,7 +4,7 @@ comments: true
# 小结
- 数组和链表是两种基本数据结构,代表了数据在计算机内存中的两种存储方式,即连续空间存储和离散空间存储。两者的优缺点呈现出此消彼长的关系。
- 数组和链表是两种基本数据结构,代表了数据在计算机内存中的两种存储方式,即连续空间存储和离散空间存储。两者的优点与缺点呈现出此消彼长的关系。
- 数组支持随机访问、内存空间占用小;但插入与删除元素效率低,且初始化后长度不可变。
- 链表可通过更改指针实现高效的结点插入与删除,并且可以灵活地修改长度;但结点访问效率低、占用内存多。常见的链表类型有单向链表、循环链表、双向链表。
- 列表又称动态数组,是基于数组实现的一种数据结构,其保存了数组的优势,且可以灵活改变长度。列表的出现大大提升了数组的实用性,但副作用是会造成部分内存空间浪费。

View File

@@ -149,6 +149,22 @@ comments: true
}
```
=== "Swift"
```swift title="leetcode_two_sum.swift"
func twoSumBruteForce(nums: [Int], target: Int) -> [Int] {
// 两层循环,时间复杂度 O(n^2)
for i in nums.indices.dropLast() {
for j in nums.indices.dropFirst(i + 1) {
if nums[i] + nums[j] == target {
return [i, j]
}
}
}
return [0]
}
```
### 方法二:辅助哈希表
时间复杂度 $O(N)$ ,空间复杂度 $O(N)$ ,属于「空间换时间」。
@@ -294,3 +310,20 @@ comments: true
}
}
```
=== "Swift"
```swift title="leetcode_two_sum.swift"
func twoSumHashTable(nums: [Int], target: Int) -> [Int] {
// 辅助哈希表,空间复杂度 O(n)
var dic: [Int: Int] = [:]
// 单层循环,时间复杂度 O(n)
for i in nums.indices {
if let j = dic[target - nums[i]] {
return [j, i]
}
dic[nums[i]] = i
}
return [0]
}
```

View File

@@ -92,8 +92,7 @@ $$
```c title=""
// 在某运行平台下
void algorithm(int n)
{
void algorithm(int n) {
int a = 2; // 1 ns
a = a + 1; // 1 ns
a = a * 2; // 10 ns
@@ -243,20 +242,17 @@ $$
```c title=""
// 算法 A 时间复杂度:常数阶
void algorithm_A(int n)
{
void algorithm_A(int n) {
printf("%d", 0);
}
// 算法 B 时间复杂度:线性阶
void algorithm_B(int n)
{
void algorithm_B(int n) {
for (int i = 0; i < n; i++) {
printf("%d", 0);
}
}
// 算法 C 时间复杂度:常数阶
void algorithm_C(int n)
{
void algorithm_C(int n) {
for (int i = 0; i < 1000000; i++) {
printf("%d", 0);
}
@@ -401,8 +397,7 @@ $$
=== "C"
```c title=""
void algorithm(int n)
{
void algorithm(int n) {
int a = 1; // +1
a = a + 1; // +1
a = a * 2; // +1
@@ -579,8 +574,7 @@ $$
=== "C"
```c title=""
void algorithm(int n)
{
void algorithm(int n) {
int a = 1; // +0技巧 1
a = a + n; // +0技巧 1
// +n技巧 2
@@ -749,12 +743,11 @@ $$
```c title="time_complexity.c"
/* 常数阶 */
int constant(int n)
{
int constant(int n) {
int count = 0;
int size = 100000;
int i = 0;
for(int i = 0; i < size; i++){
for (int i = 0; i < size; i++) {
count ++;
}
return count;
@@ -857,10 +850,9 @@ $$
```c title="time_complexity.c"
/* 线性阶 */
int linear(int n)
{
int linear(int n) {
int count = 0;
for(int i = 0; i < n; i++){
for (int i = 0; i < n; i++) {
count ++;
}
return count;
@@ -969,11 +961,10 @@ $$
```c title="time_complexity.c"
/* 线性阶(遍历数组) */
int arrayTraversal(int *nums, int n)
{
int arrayTraversal(int *nums, int n) {
int count = 0;
// 循环次数与数组长度成正比
for(int i = 0; i < n; i++){
for (int i = 0; i < n; i++) {
count ++;
}
return count;
@@ -1091,12 +1082,11 @@ $$
```c title="time_complexity.c"
/* 平方阶 */
int quadratic(int n)
{
int quadratic(int n) {
int count = 0;
// 循环次数与数组长度成平方关系
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
count ++;
}
}
@@ -1253,14 +1243,12 @@ $$
```c title="time_complexity.c"
/* 平方阶(冒泡排序) */
int bubbleSort(int *nums, int n)
{
int bubbleSort(int *nums, int n) {
int count = 0; // 计数器
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for(int i = n - 1; i > 0; i--){
for (int i = n - 1; i > 0; i--) {
// 内循环:冒泡操作
for (int j = 0; j < i; j++)
{
for (int j = 0; j < i; j++) {
// 交换 nums[j] 与 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
@@ -1417,15 +1405,12 @@ $$
```c title="time_complexity.c"
/* 指数阶(循环实现) */
int exponential(int n)
{
int exponential(int n) {
int count = 0;
int bas = 1;
// cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1)
for (int i = 0; i < n; i++)
{
for (int j = 0; j < bas; j++)
{
for (int i = 0; i < n; i++) {
for (int j = 0; j < bas; j++) {
count++;
}
bas *= 2;
@@ -1538,8 +1523,7 @@ $$
```c title="time_complexity.c"
/* 指数阶(递归实现) */
int expRecur(int n)
{
int expRecur(int n) {
if (n == 1) return 1;
return expRecur(n - 1) + expRecur(n - 1) + 1;
}
@@ -1646,11 +1630,9 @@ $$
```c title="time_complexity.c"
/* 对数阶(循环实现) */
int logarithmic(float n)
{
int logarithmic(float n) {
int count = 0;
while (n > 1)
{
while (n > 1) {
n = n / 2;
count++;
}
@@ -1752,8 +1734,7 @@ $$
```c title="time_complexity.c"
/* 对数阶(递归实现) */
int logRecur(float n)
{
int logRecur(float n) {
if (n <= 1) return 0;
return logRecur(n / 2) + 1;
}
@@ -1864,13 +1845,11 @@ $$
```c title="time_complexity.c"
/* 线性对数阶 */
int linearLogRecur(float n)
{
int linearLogRecur(float n) {
if (n <= 1) return 1;
int count = linearLogRecur(n / 2) +
linearLogRecur(n / 2);
for (int i = 0; i < n; i++)
{
for (int i = 0; i < n; i++) {
count ++;
}
return count;
@@ -2000,12 +1979,10 @@ $$
```c title="time_complexity.c"
/* 阶乘阶(递归实现) */
int factorialRecur(int n)
{
int factorialRecur(int n) {
if (n == 0) return 1;
int count = 0;
for (int i = 0; i < n; i++)
{
for (int i = 0; i < n; i++) {
count += factorialRecur(n - 1);
}
return count;
@@ -2226,18 +2203,15 @@ $$
```c title="worst_best_time_complexity.c"
/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */
int *randomNumbers(int n)
{
int *randomNumbers(int n) {
// 分配堆区内存创建一维可变长数组数组中元素数量为n元素类型为int
int *nums = (int *)malloc(n * sizeof(int));
// 生成数组 nums = { 1, 2, 3, ..., n }
for (int i = 0; i < n; i++)
{
for (int i = 0; i < n; i++) {
nums[i] = i + 1;
}
// 随机打乱数组元素
for (int i = n - 1; i > 0; i--)
{
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
int temp = nums[i];
nums[i] = nums[j];
@@ -2247,33 +2221,26 @@ $$
}
/* 查找数组 nums 中数字 1 所在索引 */
int findOne(int *nums, int n)
{
for (int i = 0; i < n; i++)
{
int findOne(int *nums, int n) {
for (int i = 0; i < n; i++) {
if (nums[i] == 1) return i;
}
return -1;
}
/* Driver Code */
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
// 初始化随机数种子
srand((unsigned int)time(NULL));
for (int i = 0; i < 10; i++)
{
for (int i = 0; i < 10; i++) {
int n = 100;
int *nums = randomNumbers(n);
int index = findOne(nums, n);
printf("\n数组 [ 1, 2, ..., n ] 被打乱后 = ");
for (int j = 0; j < n; j++)
{
printf("%d%s", nums[j], (j == n-1)? "" : "," );
}
printf("\n数字 1 的索引为 %d\n", index);
printArray(nums, n);
printf("数字 1 的索引为 %d\n", index);
// 释放堆区内存
if(nums != NULL){
if (nums != NULL) {
free(nums);
nums = NULL;
}

View File

@@ -82,10 +82,118 @@ comments: true
## 风格约定
- 文章中的重要名词会用「」符号标注,例如「数组 Array」。名词混淆会导致不必要的歧义因此最好可以记住这类名词包括中文和英文以便后续阅读文献时使用。
- 重点内容、总起句、总结句会被 **加粗** ,此类文字值得更多关注。
- 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。
- 标题后标注 * 符号的是选读章节,如果你的时间有限,可以先跳过这些章节。
- 文章中的重要名词会用「」符号标注,例如「数组 Array」。名词混淆会导致不必要的歧义因此最好可以记住这类名词包括中文和英文以便后续阅读文献时使用。
- 重点内容、总起句、总结句会被 **加粗** ,此类文字值得特别关注。
- 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。
- 在工程应用中,每种语言都有注释规范;而本书放弃了一部分的注释规范性,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。
=== "Java"
```java title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
=== "C++"
```cpp title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
=== "Python"
```python title=""
""" 标题注释,用于标注函数、类、测试样例等 """
# 内容注释,用于详解代码
"""
多行
注释
"""
```
=== "Go"
```go title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
=== "JavaScript"
```js title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
=== "TypeScript"
```typescript title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
=== "C"
```c title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
=== "C#"
```csharp title=""
/* 标题注释,用于标注函数、类、测试样例等 */
// 内容注释,用于详解代码
/**
* 多行
* 注释
*/
```
"""
在 Java, C, C++, C#, Go, JS, TS 的代码注释中,`/* ... */` 用于注释函数、类、测试样例等标题, `// ...` 用于解释代码内容;类似地,在 Python 中,`""" ... """` 用于注释标题, `# ...` 用于解释代码。
## 本书特点 *

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -466,7 +466,7 @@ $$
**空间复杂度 $O(1)$ ** 指针 `i` , `j` 使用常数大小空间。
## 优缺点
## 优点与缺点
二分查找效率很高,体现在:

View File

@@ -185,7 +185,7 @@ comments: true
**空间复杂度:** $O(n)$ ,其中 $n$ 为数组或链表长度。
## 优缺点
## 优点与缺点
在哈希表中,**查找、插入、删除操作的平均时间复杂度都为 $O(1)$** ,这意味着无论是高频增删还是高频查找场景,哈希查找的性能表现都非常好。当然,一切的前提是保证哈希表未退化。

View File

@@ -244,7 +244,7 @@ comments: true
**空间复杂度 $O(1)$ ** 无需使用额外空间。
## 优缺点
## 优点与缺点
**线性查找的通用性极佳。** 由于线性查找是依次访问元素的,即没有跳跃访问元素,因此数组或链表皆适用。

View File

@@ -328,7 +328,7 @@ comments: true
queSize++;
}
/* 出队 */
int poll() {
void poll() {
int num = peek();
// 删除头结点
ListNode *tmp = front;
@@ -336,7 +336,6 @@ comments: true
// 释放内存
delete tmp;
queSize--;
return num;
}
/* 访问队首元素 */
int peek() {
@@ -719,11 +718,10 @@ comments: true
rear = (rear + 1) % capacity();
}
/* 出队 */
int poll() {
void poll() {
int num = peek();
// 队头指针向后移动一位,若越过尾部则返回到数组头部
front = (front + 1) % capacity();
return num;
}
/* 访问队首元素 */
int peek() {

View File

@@ -236,7 +236,9 @@ comments: true
### 基于链表的实现
使用「链表」实现栈时,将链表的结点看作栈顶即可
使用「链表」实现栈时,将链表的结点看作栈顶,尾结点看作栈底
对于入栈操作,将元素插入到链表头部即可,这种结点添加方式被称为“头插法”。而对于出栈操作,则将头结点从链表中删除即可。
受益于链表的离散存储方式,栈的扩容更加灵活,删除元素的内存也会被系统自动回收;缺点是无法像数组一样高效地随机访问,并且由于链表结点需存储指针,导致单个元素占用空间更大。
@@ -311,14 +313,13 @@ comments: true
stkSize++;
}
/* 出栈 */
int pop() {
void pop() {
int num = top();
ListNode *tmp = stackTop;
stackTop = stackTop->next;
// 释放内存
delete tmp;
stkSize--;
return num;
}
/* 访问栈顶元素 */
int top() {
@@ -592,7 +593,7 @@ comments: true
### 基于数组的实现
使用「数组」实现栈时,将数组的尾部当作栈顶。准确地说,我们需要使用「列表」,因为入栈的元素可能是源源不断的,因此使用动态数组可以方便扩容
使用「数组」实现栈时,将数组的尾部当作栈顶,这样可以保证入栈与出栈操作的时间复杂度都为 $O(1)$ 。准确地说,由于入栈的元素可能是源源不断的,我们需要使用可以动态扩容的「列表」
基于数组实现的栈,优点是支持随机访问,缺点是会造成一定的空间浪费,因为列表的容量始终 $\geq$ 元素数量。
@@ -655,10 +656,9 @@ comments: true
stack.push_back(num);
}
/* 出栈 */
int pop() {
void pop() {
int oldTop = top();
stack.pop_back();
return oldTop;
}
/* 访问栈顶元素 */
int top() {

View File

@@ -120,7 +120,7 @@ comments: true
- 「根结点 Root Node」二叉树最顶层的结点其没有父结点
- 「叶结点 Leaf Node」没有子结点的结点其两个指针都指向 $\text{null}$
- 结点所处「层 Level」从顶底依次增加,根结点所处层为 1
- 结点所处「层 Level」从顶底依次增加,根结点所处层为 1
- 结点「度 Degree」结点的子结点数量。二叉树中度的范围是 0, 1, 2
- 「边 Edge」连接两个结点的边即结点指针
- 二叉树「高度」:二叉树中根结点到最远叶结点走过边的数量;

View File

@@ -10,7 +10,7 @@ hide:
![conceptual_rendering](index.assets/conceptual_rendering.png){ align=left width=350 }
</br></br></br></br></br>
<h1 align="center"> 《 Hello算法 》 </h1>
<p align="center"> 动画图解、能运行、可讨论的</br>数据结构与算法快速入门教程 </p>
<p align="center"> 动画图解、能运行、可提问的</br>数据结构与算法快速入门教程 </p>
<p align="center"> [![github-stars](https://img.shields.io/github/stars/krahets/hello-algo?style=social)](https://github.com/krahets/hello-algo)</p>
<h6 align="center"> [@Krahets](https://leetcode.cn/u/jyd/) </h6>
</div>