如何将一颗二叉树按层打印,一层输出到一行上?
随机结构的有序链表
通过建立索引的方式,对于数据量越大的有序链表,通过建立多级索引,查找效率提升会非常明显。
一般高度都设定为 l o g 2 n log_2 n log2n!
二分法也是 l o g 2 n log_2 n log2n,那么为什么要用跳表呢?
核心思想:比较相邻的元素。如果第一个比第二个大,就交换他们两个。
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值: C m i n = n − 1 , M m i n = 0 C_{min}=n-1,M_{min}=0 Cmin=n−1,Mmin=0,所以,冒泡排序最好的时间复杂度为 O ( n ) O(n) O(n)。
若初始文件是反序的,需要进行 n − 1 n-1 n−1趟排序。每趟排序要进行 n − i n-i n−i次关键字的比较( 1 ≤ i ≤ n − 1 1≤i≤n-1 1≤i≤n−1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
C m a x = n ( n − 1 ) 2 = O ( n 2 ) M m a x = 3 n ( n − 1 ) 2 = O ( n 2 ) C_{max}=\frac{n(n-1)}{2}=O(n^2)\\ M_{max}=\frac{3n(n-1)}{2}=O(n^2) Cmax=2n(n−1)=O(n2)Mmax=23n(n−1)=O(n2)
冒泡排序的最坏时间复杂度为 O ( N 2 ) O(N^2) O(N2),综上,因此冒泡排序总的平均时间复杂度为 O ( N 2 ) O(N^2) O(N2)。
冒泡排序在排序的过程中,不需要占用很多额外的空间(就是在交换元素的时候需要临时变量存一存,这里需要的额外空间开销是常量级的),因此冒泡排序的空间复杂度为 O ( 1 ) O(1) O(1)了。
#include
void BubbleSort(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
int i,j,temp;
for (i = 0; i < nLength - 1; i++) {
for(j = 0; j < nLength -1 - i;j++){
if (arr[j]>arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void Print(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
for (int i = 0; i < nLength; i++) {
printf("%d ",arr[i]);
}
printf("\n");
}
int main(){
int arr[] = {10,7,8,19,2,11,66,4};
BubbleSort(arr, sizeof(arr)/sizeof(arr[0]));
Print(arr, sizeof(arr)/sizeof(arr[0]));
return 0;
}
如果后面有一部分已经有序其实不需要这么多次排序,可以用一个标记记录当前组最后一次交换的下标,以其作为下一次循环的结束边界,可以避免一些无意义的比较。如果没有发生交换,不标记,也就是说此时数组已经有序,直接跳出循环。
分析:
i的范围为0到n-2,共需要完成n-1次遍历,其关系为n-2-0+1=n-1
而第i层排序的范围为i到n-2,共需要完成index-1次遍历,则n关系为n-2-i-1=index-1,得到i=n-index
而j
#include
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
平均时间复杂度: O ( N 2 ) O(N^2) O(N2)
最佳时间复杂度: O ( N 2 ) O(N^2) O(N2)
最差时间复杂度: O ( N 2 ) O(N^2) O(N2)
空间复杂度: O ( 1 ) O(1) O(1)
选择排序的交换操作介于$ 0 $和 ( n − 1 ) (n - 1) (n−1)次之间。选择排序的比较操作为 n ( n − 1 ) 2 \frac{n (n - 1)}{2} 2n(n−1)次之间。选择排序的赋值操作介于 0 0 0 和 3 ( n − 1 ) 3 (n - 1) 3(n−1) 次之间。总的比较次数 N = ( n − 1 ) + ( n − 2 ) + . . . + 1 = n ( n − 1 ) 2 N=(n-1)+(n-2)+...+1=\frac{n (n - 1)}{2} N=(n−1)+(n−2)+...+1=2n(n−1)。交换次数 O ( n ) O(n) O(n),最好情况是,已经有序,交换0次;最坏情况交换 n − 1 n-1 n−1次,逆序交换 n 2 \frac{n}{2} 2n次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。
#include
void SelectionSort(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
int i,j;
int min;
for (i = 0; i < nLength -1; i++) {
min = i;
for (j = i + 1; j < nLength; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
//将最小值放入
int temp;
temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
void Print(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
for (int i = 0; i < nLength; i++) {
printf("%d ",arr[i]);
}
printf("\n");
}
int main(){
int arr[] = {10,7,8,19,2,11,66,74,88};
SelectionSort(arr, sizeof(arr)/sizeof(arr[0]));
Print(arr, sizeof(arr)/sizeof(arr[0]));
return 0;
}
基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。(也就是:将待排序数据分成两个部分,一部分有序,一部分无序,将无序元素依次插入到有序中去,完成排序)
插入排序的平均时间复杂度是 O ( N 2 ) O(N^2) O(N2),空间复杂度为常数阶 O ( 1 ) O(1) O(1),具体时间复杂度和有序性有关联。
插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较 N − 1 N-1 N−1次,时间复杂度为 O ( N ) O(N) O(N)。最坏的情况是待排序数组是逆序的,此时需要比较次数最多,最坏的情况是 O ( N 2 ) O(N^2) O(N2)。
步骤:
1、分为有序/无序俩部分
2、无序插入到有序中去(倒序遍历有序的)
#include
void InsertionSort(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
int i,j;
i = 1;
int temp;
//无序元素插入
for (i = 0; i < nLength; i++) {
j = i - 1;
temp = arr[i];
//倒序遍历有序数组 插入无序元素
//while(temp < arr[j] && j >= 0){
while(j >= 0 && temp < arr[j]){
//不可能无休止的往前遍历,注意有序数组j限制
//移动
arr[j + 1] = arr[j];
j--;
//注意 j--后,j = -1时
//temp < arr[j] && j >= 0中temp < arr[j]会产生越界访问
//所以需要改成j >= 0 && temp < arr[j]
}
//插入
arr[j + 1] = temp;
}
}
void Print(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
for (int i = 0; i < nLength; i++) {
printf("%d ",arr[i]);
}
printf("\n");
}
int main(){
int arr[] = {10,7,8,19,2,11,6,4,88};
InsertionSort(arr, sizeof(arr)/sizeof(arr[0]));
Print(arr, sizeof(arr)/sizeof(arr[0]));
return 0;
}
内涵了插入排序的思想,以分块的形式放开了数据量的限制,是插入排序的优化体。把记录按下标的一定增量分组,对每组使用直接插入排序算法排序。
shell排序的时间复杂度是根据选中的 增量d 有关的,所以分析shell排序的时间复杂度是个比较麻烦的事;只给出答案,不会推算;
在最优的情况下,时间复杂度为: O ( N 1.3 ) O(N^{1.3}) O(N1.3) (元素已经排序好顺序)
在最差的情况下,时间复杂度为: O ( N 2 ) O(N^2) O(N2)
空间复杂度为常数阶 O ( 1 ) O(1) O(1)。
步骤过程:
1、确定Gap
2、分组
0 0+Gap 0+Gap+Gap…… < n
1 1+Gap 1+Gap+Gap…… < n
……
3、各组进行插入排序(此时的调整位数应为+Gap or -Gap)
4、缩减Gap继续重复23步骤,最后一次分组Gap=1的插入排序进行完后算法结束。
代码:
#include
void ShellSort(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
int i,j;
int nGap;
int k;
int temp;
//确认间隔,此程序以2分方式为例
for (nGap = nLength / 2; nGap >= 1; nGap /= 2) {
//组
for (i = 0; i < nGap; i++) {
//各组内插入排序
for (j = i + nGap; j < nLength; j += nGap) {
k = j - nGap; //有序的最后一个元素
temp = arr[j];
while (k >= i && temp < arr[k]) {
arr[k + nGap] = arr[k]; //移动
k -= nGap; //往前走
}
//插入
arr[k + nGap] = temp;
}
}
}
}
void Print(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
for (int i = 0; i < nLength; i++) {
printf("%d ",arr[i]);
}
printf("\n");
}
int main(){
int arr[] = {10,7,8,19,2,11,66,74,88};
ShellSort(arr, sizeof(arr)/sizeof(arr[0]));
Print(arr, sizeof(arr)/sizeof(arr[0]));
return 0;
}
实际上,并不需要一定等第一组执行完后再对第二组排序,多组可以同时进行排序,nGap间隔进行并不相互影响,所以在代码上还可进行一些优化,可以少写一层循环,提高系统的效率,不过时间效率上是一样的。
for (i = nGap; i < nLength; i++) {
j = i - nGap;
temp = arr[i];
while(j >= 0 && temp < arr[j]){
arr[j + nGap] = arr[j];
j -= nGap;
}
//插入
arr[j + nGap] = temp;
}
适用于有一定区间,多重复,数据差值小的情况。是基于非比较的排序。
过程:
1、Max-Min确定区间范围,申请计数器数组
2、计数
3、输出
优化:如果是成绩排名,或者有对应情况的,那么只输出值会失去原有信息,所以要优化
1、Max-Min确定区间范围,申请计数器数组
2、计数,累加
3、申请新空间,然后倒序遍历数据,根据计数器数组累加后得到的排名放入对应位置
#include
#include
#include
#include
void CountingSort(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
//最大值最小值的查找,确定范围
int nMax = arr[0];
int nMin = arr[0];
for (int i = 1; i < nLength; i++) {
if (arr[i] > nMax) {
nMax = arr[i];
}
if (arr[i] < nMin) {
nMin = arr[i];
}
}
//计数数组
int *pCount = NULL;
pCount = (int*)malloc(sizeof(int)*(nMax - nMin + 1));
memset(pCount,0,sizeof(int)*(nMax - nMin + 1));
//计数
for (int i = 0; i < nLength; i++) {
pCount[arr[i] - nMin]++;
}
//累加
for (int i = 1; i < nMax - nMin + 1; i++) {
pCount[i] += pCount[i - 1];
}
//申请新空间 存储
int *pNew = (int*)malloc(sizeof(int)*nLength);
int index;
for (int i = nLength - 1; i >= 0; i--) {
//pNew[pCount[arr[i] - nMin] - 1] = arr[i];
//pCount[arr[i] - nMin] 累加后得到的排名
//-1是因为pNew数组下标
//因为存储完对应计数器要--,所以加一个index
index = pCount[arr[i] - nMin] - 1;
pCount[arr[i] - nMin]--;
pNew[index] = arr[i];
}
//pNew就是要得到的排序结果,可以根据情况看要不要拷贝覆盖源数据
//如果要拷贝
for (int i = 0; i < nLength; i++) {
arr[i] = pNew[i];
}
free(pNew);
pNew = NULL;
}
void Print(int arr[],int nLength){
if(arr == NULL || nLength <= 0)
return;
for (int i = 0; i < nLength; i++) {
printf("%d ",arr[i]);
}
printf("\n");
}
int main(){
int arr[] = {90,87,98,89,92,91,96,94,98};
CountingSort(arr, sizeof(arr)/sizeof(arr[0]));
Print(arr, sizeof(arr)/sizeof(arr[0]));
return 0;
}