Merge branch 'krahets:master' into master
@@ -245,6 +245,8 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
for (int i = 0; i < size; i++) {
|
||||
res[i] = nums[i];
|
||||
}
|
||||
// 释放内存
|
||||
delete[] nums;
|
||||
// 返回扩展后的新数组
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -748,6 +748,11 @@ comments: true
|
||||
nums = new int[numsCapacity];
|
||||
}
|
||||
|
||||
/* 析构函数 */
|
||||
~MyList() {
|
||||
delete[] nums;
|
||||
}
|
||||
|
||||
/* 获取列表长度(即当前元素数量)*/
|
||||
int size() {
|
||||
return numsSize;
|
||||
@@ -818,14 +823,14 @@ comments: true
|
||||
void extendCapacity() {
|
||||
// 新建一个长度为 size * extendRatio 的数组,并将原数组拷贝到新数组
|
||||
int newCapacity = capacity() * extendRatio;
|
||||
int* extend = new int[newCapacity];
|
||||
int* tmp = nums;
|
||||
nums = new int[newCapacity];
|
||||
// 将原数组中的所有元素复制到新数组
|
||||
for (int i = 0; i < size(); i++) {
|
||||
extend[i] = nums[i];
|
||||
nums[i] = tmp[i];
|
||||
}
|
||||
int* temp = nums;
|
||||
nums = extend;
|
||||
delete[] temp;
|
||||
// 释放内存
|
||||
delete[] tmp;
|
||||
numsCapacity = newCapacity;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -507,7 +507,7 @@ $$
|
||||
const int a = 0;
|
||||
int b = 0;
|
||||
vector<int> nums(10000);
|
||||
ListNode* node = new ListNode(0);
|
||||
ListNode node(0);
|
||||
// 循环中的变量占用 O(1) 空间
|
||||
for (int i = 0; i < n; i++) {
|
||||
int c = 0;
|
||||
@@ -654,9 +654,9 @@ $$
|
||||
// 长度为 n 的数组占用 O(n) 空间
|
||||
vector<int> nums(n);
|
||||
// 长度为 n 的列表占用 O(n) 空间
|
||||
vector<ListNode*> nodes;
|
||||
vector<ListNode> nodes;
|
||||
for (int i = 0; i < n; i++) {
|
||||
nodes.push_back(new ListNode(i));
|
||||
nodes.push_back(ListNode(i));
|
||||
}
|
||||
// 长度为 n 的哈希表占用 O(n) 空间
|
||||
unordered_map<int, string> map;
|
||||
|
||||
@@ -606,6 +606,7 @@ comments: true
|
||||
// 判空处理
|
||||
if h.isEmpty() {
|
||||
fmt.Println("error")
|
||||
return nil
|
||||
}
|
||||
// 交换根结点与最右叶结点(即交换首元素与尾元素)
|
||||
h.swap(0, h.size()-1)
|
||||
@@ -709,10 +710,10 @@ comments: true
|
||||
```go title="my_heap.go"
|
||||
/* 构造函数,根据切片建堆 */
|
||||
func newMaxHeap(nums []any) *maxHeap {
|
||||
// 所有元素入堆
|
||||
// 将列表元素原封不动添加进堆
|
||||
h := &maxHeap{data: nums}
|
||||
// 堆化除叶结点以外的其他所有结点
|
||||
for i := len(h.data) - 1; i >= 0; i-- {
|
||||
// 堆化除叶结点以外的其他所有结点
|
||||
h.siftDown(i)
|
||||
}
|
||||
return h
|
||||
|
||||
@@ -228,7 +228,7 @@ comments: true
|
||||
|
||||
**稳定排序**:不交换相等元素。
|
||||
|
||||
**自适排序**:引入 `flag` 优化后(见下文),最佳时间复杂度为 $O(N)$ 。
|
||||
**自适应排序**:引入 `flag` 优化后(见下文),最佳时间复杂度为 $O(N)$ 。
|
||||
|
||||
## 效率优化
|
||||
|
||||
|
||||
@@ -16,20 +16,28 @@ comments: true
|
||||
|
||||
=== "Step 1"
|
||||

|
||||
|
||||
=== "Step 2"
|
||||

|
||||
|
||||
=== "Step 3"
|
||||

|
||||
|
||||
=== "Step 4"
|
||||

|
||||
|
||||
=== "Step 5"
|
||||

|
||||
|
||||
=== "Step 6"
|
||||

|
||||
|
||||
=== "Step 7"
|
||||

|
||||
|
||||
=== "Step 8"
|
||||

|
||||
|
||||
=== "Step 9"
|
||||

|
||||
|
||||
@@ -134,27 +142,27 @@ comments: true
|
||||
``` js title="quick_sort.js"
|
||||
/* 元素交换 */
|
||||
function swap(nums, i, j) {
|
||||
let tmp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = tmp
|
||||
let tmp = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = tmp;
|
||||
}
|
||||
|
||||
/* 哨兵划分 */
|
||||
function partition(nums, left, right){
|
||||
function partition(nums, left, right) {
|
||||
// 以 nums[left] 作为基准数
|
||||
let i = left, j = right
|
||||
while(i < j){
|
||||
while(i < j && nums[j] >= nums[left]){
|
||||
j -= 1 // 从右向左找首个小于基准数的元素
|
||||
let i = left, j = right;
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left]) {
|
||||
j -= 1; // 从右向左找首个小于基准数的元素
|
||||
}
|
||||
while(i < j && nums[i] <= nums[left]){
|
||||
i += 1 // 从左向右找首个大于基准数的元素
|
||||
while (i < j && nums[i] <= nums[left]) {
|
||||
i += 1; // 从左向右找首个大于基准数的元素
|
||||
}
|
||||
// 元素交换
|
||||
swap(nums, i, j) // 交换这两个元素
|
||||
swap(nums, i, j); // 交换这两个元素
|
||||
}
|
||||
swap(nums, i, left) // 将基准数交换至两子数组的分界线
|
||||
return i // 返回基准数的索引
|
||||
swap(nums, i, left); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
}
|
||||
```
|
||||
|
||||
@@ -220,7 +228,6 @@ comments: true
|
||||
swap(nums, i, left); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
@@ -313,14 +320,14 @@ comments: true
|
||||
|
||||
```js title="quick_sort.js"
|
||||
/* 快速排序 */
|
||||
function quickSort(nums, left, right){
|
||||
function quickSort(nums, left, right) {
|
||||
// 子数组长度为 1 时终止递归
|
||||
if(left >= right) return
|
||||
if (left >= right) return;
|
||||
// 哨兵划分
|
||||
const pivot = partition(nums, left, right)
|
||||
const pivot = partition(nums, left, right);
|
||||
// 递归左子数组、右子数组
|
||||
quick_sort(nums, left, pivot - 1)
|
||||
quick_sort(nums, pivot + 1, right)
|
||||
quickSort(nums, left, pivot - 1);
|
||||
quickSort(nums, pivot + 1, right);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -408,7 +415,7 @@ comments: true
|
||||
int medianThree(int[] nums, int left, int mid, int right) {
|
||||
// 使用了异或操作来简化代码
|
||||
// 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if ((nums[left] > nums[mid]) ^ (nums[left] > nums[right]))
|
||||
if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right]))
|
||||
return left;
|
||||
else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right]))
|
||||
return mid;
|
||||
@@ -434,7 +441,7 @@ comments: true
|
||||
int medianThree(vector<int>& nums, int left, int mid, int right) {
|
||||
// 使用了异或操作来简化代码
|
||||
// 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if ((nums[left] > nums[mid]) ^ (nums[left] > nums[right]))
|
||||
if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right]))
|
||||
return left;
|
||||
else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right]))
|
||||
return mid;
|
||||
@@ -460,7 +467,7 @@ comments: true
|
||||
def median_three(self, nums, left, mid, right):
|
||||
# 使用了异或操作来简化代码
|
||||
# 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if (nums[left] > nums[mid]) ^ (nums[left] > nums[right]):
|
||||
if (nums[left] < nums[mid]) ^ (nums[left] < nums[right]):
|
||||
return left
|
||||
elif (nums[mid] < nums[left]) ^ (nums[mid] > nums[right]):
|
||||
return mid
|
||||
@@ -481,9 +488,9 @@ comments: true
|
||||
```go title="quick_sort.go"
|
||||
/* 选取三个元素的中位数 */
|
||||
func medianThree(nums []int, left, mid, right int) int {
|
||||
if (nums[left] > nums[mid]) != (nums[left] > nums[right]) {
|
||||
if (nums[left] < nums[mid]) != (nums[left] < nums[right]) {
|
||||
return left
|
||||
} else if (nums[mid] < nums[left]) != (nums[mid] > nums[right]) {
|
||||
} else if (nums[mid] > nums[left]) != (nums[mid] > nums[right]) {
|
||||
return mid
|
||||
}
|
||||
return right
|
||||
@@ -507,7 +514,7 @@ comments: true
|
||||
function medianThree(nums, left, mid, right) {
|
||||
// 使用了异或操作来简化代码
|
||||
// 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if ((nums[left] > nums[mid]) ^ (nums[left] > nums[right]))
|
||||
if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right]))
|
||||
return left;
|
||||
else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right]))
|
||||
return mid;
|
||||
@@ -533,7 +540,7 @@ comments: true
|
||||
function medianThree(nums: number[], left: number, mid: number, right: number): number {
|
||||
// 使用了异或操作来简化代码
|
||||
// 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if (Number(nums[left] > nums[mid]) ^ Number(nums[left] > nums[right])) {
|
||||
if (Number(nums[left] < nums[mid]) ^ Number(nums[left] < nums[right])) {
|
||||
return left;
|
||||
} else if (Number(nums[mid] < nums[left]) ^ Number(nums[mid] < nums[right])) {
|
||||
return mid;
|
||||
@@ -566,7 +573,7 @@ comments: true
|
||||
{
|
||||
// 使用了异或操作来简化代码
|
||||
// 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if ((nums[left] > nums[mid]) ^ (nums[left] > nums[right]))
|
||||
if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right]))
|
||||
return left;
|
||||
else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right]))
|
||||
return mid;
|
||||
|
||||
@@ -196,5 +196,29 @@ comments: true
|
||||
=== "Swift"
|
||||
|
||||
```swift title="deque.swift"
|
||||
|
||||
/* 初始化双向队列 */
|
||||
// Swift 没有内置的双向队列类,可以把 Array 当作双向队列来使用
|
||||
var deque: [Int] = []
|
||||
|
||||
/* 元素入队 */
|
||||
deque.append(2) // 添加至队尾
|
||||
deque.append(5)
|
||||
deque.append(4)
|
||||
deque.insert(3, at: 0) // 添加至队首
|
||||
deque.insert(1, at: 0)
|
||||
|
||||
/* 访问元素 */
|
||||
let peekFirst = deque.first! // 队首元素
|
||||
let peekLast = deque.last! // 队尾元素
|
||||
|
||||
/* 元素出队 */
|
||||
// 使用 Array 模拟时 pollFirst 的复杂度为 O(n)
|
||||
let pollFirst = deque.removeFirst() // 队首元素出队
|
||||
let pollLast = deque.removeLast() // 队尾元素出队
|
||||
|
||||
/* 获取双向队列的长度 */
|
||||
let size = deque.count
|
||||
|
||||
/* 判断双向队列是否为空 */
|
||||
let isEmpty = deque.isEmpty
|
||||
```
|
||||
|
||||
@@ -246,6 +246,7 @@ comments: true
|
||||
let peek = queue.first!
|
||||
|
||||
/* 元素出队 */
|
||||
// 使用 Array 模拟时 poll 的复杂度为 O(n)
|
||||
let pool = queue.removeFirst()
|
||||
|
||||
/* 获取队列的长度 */
|
||||
@@ -330,6 +331,10 @@ comments: true
|
||||
rear = nullptr;
|
||||
queSize = 0;
|
||||
}
|
||||
~LinkedListQueue() {
|
||||
delete front;
|
||||
delete rear;
|
||||
}
|
||||
/* 获取队列的长度 */
|
||||
int size() {
|
||||
return queSize;
|
||||
@@ -784,6 +789,9 @@ comments: true
|
||||
cap = capacity;
|
||||
nums = new int[capacity];
|
||||
}
|
||||
~ArrayQueue() {
|
||||
delete[] nums;
|
||||
}
|
||||
/* 获取队列的容量 */
|
||||
int capacity() {
|
||||
return cap;
|
||||
|
||||
@@ -324,6 +324,9 @@ comments: true
|
||||
stackTop = nullptr;
|
||||
stkSize = 0;
|
||||
}
|
||||
~LinkedListStack() {
|
||||
freeMemoryLinkedList(stackTop);
|
||||
}
|
||||
/* 获取栈的长度 */
|
||||
int size() {
|
||||
return stkSize;
|
||||
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
@@ -22,19 +22,15 @@ comments: true
|
||||
- 若 `cur.val = num` ,说明找到目标结点,跳出循环并返回该结点即可;
|
||||
|
||||
=== "Step 1"
|
||||
|
||||

|
||||
|
||||
=== "Step 2"
|
||||
|
||||

|
||||
|
||||
=== "Step 3"
|
||||
|
||||

|
||||
|
||||
=== "Step 4"
|
||||
|
||||

|
||||
|
||||
二叉搜索树的查找操作和二分查找算法如出一辙,也是在每轮排除一半情况。循环次数最多为二叉树的高度,当二叉树平衡时,使用 $O(\log n)$ 时间。
|
||||
@@ -483,9 +479,9 @@ comments: true
|
||||
// 找到待删除结点,跳出循环
|
||||
if (cur.val == num) break;
|
||||
pre = cur;
|
||||
// 待删除结点在 root 的右子树中
|
||||
// 待删除结点在 cur 的右子树中
|
||||
if (cur.val < num) cur = cur.right;
|
||||
// 待删除结点在 root 的左子树中
|
||||
// 待删除结点在 cur 的左子树中
|
||||
else cur = cur.left;
|
||||
}
|
||||
// 若无待删除结点,则直接返回
|
||||
@@ -527,9 +523,9 @@ comments: true
|
||||
// 找到待删除结点,跳出循环
|
||||
if (cur->val == num) break;
|
||||
pre = cur;
|
||||
// 待删除结点在 root 的右子树中
|
||||
// 待删除结点在 cur 的右子树中
|
||||
if (cur->val < num) cur = cur->right;
|
||||
// 待删除结点在 root 的左子树中
|
||||
// 待删除结点在 cur 的左子树中
|
||||
else cur = cur->left;
|
||||
}
|
||||
// 若无待删除结点,则直接返回
|
||||
@@ -575,9 +571,9 @@ comments: true
|
||||
if cur.val == num:
|
||||
break
|
||||
pre = cur
|
||||
if cur.val < num: # 待删除结点在 root 的右子树中
|
||||
if cur.val < num: # 待删除结点在 cur 的右子树中
|
||||
cur = cur.right
|
||||
else: # 待删除结点在 root 的左子树中
|
||||
else: # 待删除结点在 cur 的左子树中
|
||||
cur = cur.left
|
||||
|
||||
# 若无待删除结点,则直接返回
|
||||
@@ -677,9 +673,9 @@ comments: true
|
||||
// 找到待删除结点,跳出循环
|
||||
if (cur.val === num) break;
|
||||
pre = cur;
|
||||
// 待删除结点在 root 的右子树中
|
||||
// 待删除结点在 cur 的右子树中
|
||||
if (cur.val < num) cur = cur.right;
|
||||
// 待删除结点在 root 的左子树中
|
||||
// 待删除结点在 cur 的左子树中
|
||||
else cur = cur.left;
|
||||
}
|
||||
// 若无待删除结点,则直接返回
|
||||
@@ -725,9 +721,9 @@ comments: true
|
||||
}
|
||||
pre = cur;
|
||||
if (cur.val < num) {
|
||||
cur = cur.right as TreeNode; // 待删除结点在 root 的右子树中
|
||||
cur = cur.right as TreeNode; // 待删除结点在 cur 的右子树中
|
||||
} else {
|
||||
cur = cur.left as TreeNode; // 待删除结点在 root 的左子树中
|
||||
cur = cur.left as TreeNode; // 待删除结点在 cur 的左子树中
|
||||
}
|
||||
}
|
||||
// 若无待删除结点,则直接返回
|
||||
@@ -780,9 +776,9 @@ comments: true
|
||||
// 找到待删除结点,跳出循环
|
||||
if (cur.val == num) break;
|
||||
pre = cur;
|
||||
// 待删除结点在 root 的右子树中
|
||||
// 待删除结点在 cur 的右子树中
|
||||
if (cur.val < num) cur = cur.right;
|
||||
// 待删除结点在 root 的左子树中
|
||||
// 待删除结点在 cur 的左子树中
|
||||
else cur = cur.left;
|
||||
}
|
||||
// 若无待删除结点,则直接返回
|
||||
|
||||
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 318 KiB After Width: | Height: | Size: 177 KiB |