C语言数据结构复习(快排,融合,选择,插入,排序)

因为最近在期末复习,就整理了一下算法,由难到易展示一下,不需要概念的直接跳到代码就好。(排序为左小右大)
请一边看这个网站的动图,加深理解
算法步骤图
前面是一些概念:

如何优秀编程:

可读性:别人看得懂
可靠性:怎么测试都可以
可维护性:接口友好
可复用性:易于被别人使用,合理注释

考虑:复杂度(空间,时间),可行度,正确度

空间复杂度和时间复杂度

空间复杂度:所占储存的体积
时间复杂度:运行语句的条数
快排的平均时间复杂度为nlogn
在这里插入图片描述

算法的伪代码形式

<, =, >, >=, <=,<>//运算符
or, and, not//逻辑运算符

start

a,b,sum, equal: Integer//定义
write('please input two numbers')//写
read(a,b)//读
sum<-a+b//赋值
write('the first number is',a,'the second is',b,'the sum is',sum)
//for 语句
for i varying from 1 to 10
	write('Hello');
end for




//if 语句
**if** a<b **then**
 equal<-  -1
**if** a>b **then**
 equal<- 1
**if not**
 equal<- 0
**end if**

//switch语句
write(‘give me a number’)
read(n)
in case of n is
0:write(‘zero’)
1:write(‘one’)
2:write(‘two’)
otherwise
write(‘too big’)
end case

//do while 语句
num: Integer
read(num)
repeat/do
num++;
while num<10//num<10时执行
until num>=10//num >=10 时退出
end while

end
接下来就可以用三种形式表示程序啦。

1-快速排序:

思路:取出最左边的数作为基准数(pivot),要达到pivot左侧比它小,右侧比它大,扫描从右侧第一和左侧第一分别进行,先看右侧,找到在右侧,但比pivot小的数,放到左边,覆盖pivot原来的位置,再找到左侧但比pivot大的数,放到右边,以此类推,直到左右指针相遇,即为pivot所在位置,再分别对pivot左右进行快排。(请查看上述链接,思路基本一样,但它的pivot从中间开始选择)

代码:
void quicksort(int low, int high,int arr[]) {
 //思想:选取一个值,把它排到它应该在的位置(这里选取第一个)
 //即它的左侧都要比它小,它的右边都要比它大
 //不满足就把右侧的数换到左边/把左边换到右边(通过一个空位)
 //直到左右相遇即为pivot的正确位置
 //再用递归分别快排pivot左侧和右侧的数组达成有序
 //直到只有一个数,退出递归
 int* left = arr+low;
 int* right = arr + high;
 int pivot = left;//选取pivot 
 //我们要达成的状态:pivot右边比他大,左边比他小
 if (low > high) {
  return;
 }//用于退出递归。
 while (left<right) {
  while (right >= pivot&&left<right) {//要是右边比他大,检验合格,下一个 
   right--;//注意还要避免在while循环中超出范围
    }
  *left = *right;//检验不合格,右边要放到左边来
  //因为设定pivot初始等于最左值,所以最左值重复了两次,原最左位可以给右边小于pivot的值
  //同时原本在右边的值已经到左边了,它现在也有两个了.
  //但我们需要的正确位置是它在pivot左侧, 所以等一下它在右边的重复值也同样被覆盖
  //其实我们设定的变量pivot相当于为原数组腾挪出了一个空位用于排序,
  //而我们最终要确定的只是pivot的正确位置
  //所以,pivot存在变量里,左右两边互相覆盖
  //left++;//因为挪过来的值一定比pivot小,所以检验合格,不再重复比较
  //错误!此步不可以随便加入,因为可能前面已经因为right--退出了小循环
  //若再加会造成left>right,所以当此时left,right就已经指向同一个时,自己交换自己是可以的
 while (left <=pivot&&left<right) {
  left++;//与右侧对称
 }
 *right = *left;//左值覆盖右值
     // right--;
}
 //最后的结局是左右指针指向同一个位置,而这个位置就是pivot的归宿
 *left = pivot;//此时left==right,写哪个都没事
 for (int i = 0; i < 10; i++) {
  printf("%d ", arr[i]);
 }
 putchar('\n');
//现在我们已经完成了一个数的快排,现在可以用递归实现两边的快排了
 int mid = left - arr;//地址值相减,得到在数组中的位置
 quicksort(low, mid-1, arr);
 quicksort(mid+ 1, high, arr);
 }

注意:
2. 如果从左选取pivot则要先把右边的数挪过来,右侧反之
3. 本质是取出pivot放在变量里再依次左右挪动
4. 注意递归和退出递归条件

算法语言

//仍是左小右大的方向
function quicksort(begin,end,tab)
//注释变量类型
begin,end:integer(I)//I -->input,O–>output
tab:array(I)
left,right,pivot:integer pointer
left<–tab+begin
right<–tab+right
pivot<–tab+begin
if begin>end then
//因为排到最后一个数时下一次begin会直接<end,
//注意不可以用==,因为下面递归时用的是left-tab-1,会直接跳过,导致递归无法中断
return

while left<>right and
while *pivot<*right and left<>right then
right–
end while
** left=right//是直接覆盖,不是交换,而且是左右值变动
while *pivot>*left and left<>right then
left++
end while
*right=*left

quicksort(begin,left-tab-1,tab)
quicksort(left-tab+1,end,tab)

end

流程图

在这里插入图片描述

2-融合排序

程序
void merge(int tab[],int begin, int end) {
 //思想:先吧数组对分到最小单元
 //再把每个最两个小单元进行融合
 //再融合更大级的单元
 //先将数组分成俩
 //merge用于将一个有序数组分成两份并融合得到更长的有序数组
 //merge_devide用于递归使数组对分
 int mid = (end+begin)/2;
 int tab1[10] = { 0 };
 int tab2[10] = { 0 };
 int p1 = 0;
 int p2 = 0;
 for (int i = begin; i <= end; i++) {
  if (i <=mid) {
   tab1[p1] = tab[i];
   p1++;
  }//一半给tab1
  else {
   tab2[p2] = tab[i];
   p2++;
  }//剩下给tab2
 }
 //用于将两个有序数组融合成一个新的有序数组
 p1 = 0;
 p2 = 0;
 int p = begin;
 int n1 = mid-begin+1;
 int n2 = end-mid;
 while (p1<n1&&p2<n2) {
 //当两个要合并的数组都没有用完
 //依次比较两数组各自位大小
 //小的放入tab,达成有序
 if (tab1[p1] < tab2[p2]) {
  tab[p] = tab1[p1];
  p1++;
  p++;
 }
 else {
  tab[p]= tab2[p2];
  p2++;
  p++;
 }
 }
 //处理剩下的数字
 if (p1 ==n1) {
  //如果第一个数组用完了
  for (p2; p2 <n2; p2++) {
   tab[p]= tab2[p2];
   p++;
  }
 }
 else {
  //第二个数组用完了
  for (p1; p1 <n1; p1++) {
   tab[p] = tab1[p1];
   p++;
  }
 }
 for (int i = begin; i <=end ; i++) {
  printf("%d ", tab[i]);
 }
 putchar('\n');
 
}
void merge_divide(int tab[],int begin,int end) {
 if (begin == end)
  return;//只剩一个数,不再分割
 int mid = (end+begin)/2;
 merge_divide(tab,begin,mid);//继续分割
 merge_divide(tab, mid+1, end);
 merge(tab, begin, end);//融合
 
}

注意:

  1. 注意融合后剩下的数字
  2. 注意递归
算法语言

function merge(tab,begin,end)
tab:Integer array(I)
begin,end:Integer(I)
tab1={0}
tab2={0}
mid<-- (begin+end)/2
for i varying from begin to end
if i<=mid then
tab_1[i]=tab[i]
if not
tab_2[i]=tab[i]
end if
end for

p1<–0
p2<–0
p<–begin
n1<–mid-begin+1
n2<–end-mid
while p1<n1 and p2<n2
if tab1[p1]>tab2[p2] then
tab[p]<–tab2[p2]
p2++
p++
if not
tab[p]<–tab1[p1]
p1++
p++
end if
end while
if p1==n1 then
while p2<n2
tab[p]=tab2[p2]
p++
p2++
end while
if not
while p1<n1
tab[p]=tab[p1]
p++
p1++
end while
end if

function divide_merge(tab,begin,end)
if begin==end then
return 0
end if
mid<–(end+begin)/2
divide_merge(tab,begin,mid)
divide_merge(tab,mid+1,end)
merge(tab,begin,end)

3-插入排序

void insert(int tab[], int num) {
 //使前面n项有序,后面的数逐渐插入前面的有序数列,有点像冒泡
 for (int n = 0; n <num; n++) {
  //控制循环次数
  for (int m = n+1 ; m >0; m--) {
   //比较该项与前一项
   if (tab[m] < tab[m-1]) {
    int replace = tab[m];
    tab[m] = tab[m-1];
    tab[m-1] = replace;
   }
  }
 }
 //打印
 for (int i = 0; i < 10; i++) {
  printf("%d ", tab[i]);
 }
 putchar('\n');
}

4-选择排序

void select(int tab[], int num) {
//思想:选择最大值与最左交换
 for (int i = 0; i < num; i++) {//无序数为0~num-i,控制循环次数
 int max = 0;//每次假设最大的是第一个
 for(int j=1;j<=num-i;j++)//需要比较第二个到无序最后一个
  if (tab[j] > tab[max]) {
   max = j;//找到最大的数字所对下标
  }
 //交换最大值和最后一个
   int replace = tab[num-i];
   tab[num-i] = tab[max];
   tab[max] = replace;
   
 }
 //打印
 for (int i = 0; i < 10; i++) {
  printf("%d ", tab[i]);
 }
 putchar('\n');
}

5-冒泡排序

void bubble(int tab[],int num) {
 //思想:从左往右,大的往右边交换
 //确保每次换到无序最左边的是最大的
 //从而达到排序
 for (int q = 0; q < num; q++) {
  //控制循环次数
  for (int j = 0; j < num - q; j++) {
   //从q开始冒泡,则此时0~num-q均无序
   //一直比较到最后两个无序数字结束 
   if (tab[j] > tab[j + 1]) {
    int replace = tab[j];
    tab[j] = tab[j + 1];
    tab[j + 1] = replace;
   }
  }
 }
 for (int i = 0; i <= num; i++) {
  printf("%d ", tab[i]);
 }
 putchar('\n');
}

注意:
可以看到最后三种排序都是用两层for实现的,值得注意的是第一层只是控制循环次数的,其局部变量不能放在第二层里,用了就错。

主函数调用:

int main() {
 int arr[] = { 23,11,56,3,0,78,12,52,9,43 };
 for (int i = 0; i < 10; i++) {
  printf("%d ", arr[i]);
 }
 putchar('\n');
 //quicksort(0, 9, arr);//快排
 //merge_divide(arr,0,9);//融合
 //bubble(arr, 9);//冒泡
 //select(arr,9);//选择
   // insert(arr, 9);//插入
 return 0;
}

菜鸟首发,欢迎讨论。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值