👋 Hi, I’m @Beast Cheng
👀 I’m interested in photography, hiking, landscape…
🌱 I’m currently learning python, javascript, kotlin…
📫 How to reach me --> 458290771@qq.com
喜欢《数据结构》部分笔记的小伙伴可以订阅专栏,今后还会不断更新。🧑💻
感兴趣的小伙伴可以点一下订阅、收藏、关注!🚀
谢谢大家!🙏
简单选择排序
思想
每一趟在待排序元素中需拿去关键字最小(最大)的元素加入有序子序列
算法实现
// 简单选择排序
void SelectSort(int A[], int n){for(int i = 0; i < n-1; i++){ // 一共进行n-1趟int min = 1; // 记录最小元素位置for(int j = i + 1; j < n; j++) // 在A[i...n-1]中选择最小的元素if(A[j] < A[min]) min = j; // 更新最小元素位置if(min != i) swap(A[i], A[min]); // 封装的swap()函数共移动元素3次}
}
算法性能分析
空间复杂度: O ( 1 ) O(1) O(1)
时间复杂度: O ( n 2 ) O(n^2) O(n2)
稳定性:不稳定
既可以用于顺序表,也可以用于链表
堆排序
什么是堆(Heap)
建立大根堆
思路:把所有非终端结点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整
在顺序存储的完全二叉树中,非终端结点编号 i ≤ ( n / 2 ) i \leq (n/2) i≤(n/2)
检查当前结点是否满足 根 ≥ 左、右 根 \geq 左、右 根≥左、右
若不满足,将当前节点与更大的一个孩子互换
代码
// 建立大根堆
void BuildMaxHeap(int A[], int len){for(int i = len/2; i > 0; i--) // 从后往前调整所有非终端结点HeadAdjust(A, i, len);
}// 将以k为根的子树调整为大根堆
void HeadAdjust(int A[], int k, int len){A[0] = A[k]; // A[0]暂存子树的根结点for(int i = 2*k; i <= len; i *= 2){ // 沿着key较大的子结点向下筛选if(i < len && A[i] < A[i + 1])i++; // 取key较大的子结点的下标if(A[0] >= A[i])break; // 筛选结束else{A[k] = A[i]; // 将A[i]调整到双亲结点上k = i; // 修改k值, 以便继续向下筛选}}A[k] = A[0]; // 被筛选结点的值放入最终位置
}
基于大根堆进行排序
堆排序: 每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)
并将待排序元素序列再次调整为大根堆(小元素不断下坠)
算法效率分析
结论:一个结点,每“下坠”一层,最多只需对比关键字两次
若树高为 h,某节点在第 i 层,则将这个节点向下调整最多只需要“下坠” h − i h - i h−i 层,关键字对比次数不超过 2 ( h − i ) 2(h-i) 2(h−i)
n 个结点的完全二叉树树高 h = l o g 2 n + 1 h=log_2n+1 h=log2n+1
每一趟排序复杂度不超过 O ( h ) = O ( l o g 2 n ) O(h)=O(log_2n) O(h)=O(log2n)
堆排序的时间复杂度: = O ( n ) + O ( n l o g 2 n ) = O ( n l o g 2 n ) =O(n)+O(nlog_2n)=O(nlog_2n) =O(n)+O(nlog2n)=O(nlog2n)
堆排序的空间复杂度: O ( 1 ) O(1) O(1)
稳定性
如果左右孩子一样大,优先和左孩子交换
结论:堆排序是不稳定的
插入删除
插入
对于小根堆,新元素放到表尾,与父结点对比,若新元素比父结点更小,则将二者互换。新元素就这样一路“上升”,直到无法继续上升为止
删除
被删除的元素用堆底元素替代,然后让该元素不断“下坠”,直到无法下坠为止