所谓的算法例如排序等,重要的不是代码,而是解题思路,思路有了,就有伪代码
和机器编译链接一样,人也只是把脑子里的思路,变成自然语言,再变各种编程语言
排序是什么?知道学习成绩排名吧,这个过程只有比较和交换
比较不用多说,大于小于判断
交换是什么,两个盒子里的蛋糕1和2,交换一下,把1拿出来放到2,2拿出来放到1
不是的,实际是把1拿出来放桌子上,2拿出来放1,再把1放2,想想过程是否如此
交换函数为啥有个中间变量,这就是那个桌子呀
void swap(int *a, int *b)
{
int temp = 0;
temp = *a;
*a = *b;
*b = temp;
}
排序问题就是解决:军训的时候一般按照个头站队,那么你怎么判断自己站哪?
1,交换排序:冒泡+快速
首先你会挨个看看个头,第一个比我矮,我往后走走,第二个比我高,我站他前面。。。
一次比较后,你会不会站进队伍去呢,你站进去了,再去看后面的个头比你矮,发现不对,
你再出来,是不是多余?
冒泡:相邻比较,符合交换,一次遍历交换出一个最值,如果遍历没有发生交换则已经有序退出
从第一个开始,看后面的人是否比自己高,到最后一个都没有发生交换,说明队伍站齐了
因为第一次没有交换说明是最矮的,后面依次是第二和第三比较,都没有发生交换
交换i从0到len-1,比较从0到len-1-i,因为最值别甩到后面已经确认了i个人的位置
//第一次遍历是n-1次比较,i=0,j<n-1-i,最好是第一次遍历n-1次比较就跳出,O(N),
//最坏就是一直到最后,N-1 + N-2 + ... +1 = O(N^2)
代码:
void BubbleSort(int[] array)
{
bool flag = true;
for (int i = 0; i < array.length - 1; i++) {
flag = false;
for (int j = 0;j < array.length -1 -i;j++) {
if (array[j] < array[j + 1]) {
swap(&array[j], &array[j+1]);
flag = true;
}
}
if (flag == false) {
break;
}
}
}
快速:不找出最值的位置,而是主元的位置,交换是跳跃的不是相邻的
思路类似于教官随便挑一个同学b,比他高的左边,比他矮的右边站
然后b不动,左边随便再找一个同学,右边随便找一个同学重复此行动
代码实现上,集体移动,是有一些存储和算法问题的,所以颠倒顺序
一次移动两个同学,也就是交换操作,左边一个右边一个交换,最后把这个同学的位置确定下
//右边开始查找比主元小的,先找最后相遇会交换,左边找比主元大的,交换,循环,
//右边先找是因为如果左边先找大于主元,最后i=j停下,交换大的在首位,可见ij相遇交换出错乱序
//最优时间复杂度递归深度log2n.+1,辅助空间O(NLogN),最差是斜树和冒泡一致,比较次数N-1 + N-2 + ... +1 = O(N^2)
代码:
void quicksort(int[] array, int left, int right)
{
int i,j,temp;
if (left > right)
return;
temp = array[left];
i = left;
j = right;
while(i!=j) {
while(a[j] >= temp && i < j)
j--;
while(a[i] <= temp && i < j)
i++;
//find i!=j swap i and j
if (i < j) {
swap(&a[i],&a[j]);
}
}
//swap i==j and left
a[left] = a[i];
a[i] = temp;
quicksort(left, i-1);
quicksort(i+1,right);
}
2,插入排序:直接插入+希尔
聪明的人,一般都是放眼望去,就看到自己应该站在哪里,因为他只比较,不交换
从后面开始看,个头比他高,那个人就得后退一格
因为要和前面的比较,i从1开始,j从i-1开始,
//插入,将一个记录插入到已经有序的表中
//插入过程:首先是0~x-1个位置,x准备插入,x之前的值下表一直右移腾出位置
//有比较,没有交换,直接赋值,每次移动一位,最好复杂度为O(N),最差O(N^2)
代码:
void InsertSort(int[] array)
{
int j;
for (int i=1; i<array.length;i++) {
j = i-1;
//插入i元素,从j开始比较,j--
int temp = array[i];
while(j>=0 && temp < array[j]) {
array[j+1] = array[j];
j--;
}
array[j+1] = temp;
}
}
希尔对插入的两点性质:几乎排好序效率高,线性;低效是因为每次只能移动一位
//分组插入,分间隔分而治之不稳定,专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中
//证明它不够快,再改成快速排序这样更高级的排序算法,就是将插入排序的1换成gap
//这样可以快速减少大量的无序情况,从而减轻后续的工作
void shellsort(int a[],int n)
{
int j,gap;
for (gap = n/2;gap > 0; gap /= 2) {
for (j = gap;j < n; j++) {
int k = j-gap;
int temp = a[j];
while (k >= 0 && a[k] > temp) {
a[k+gap] = a[k];
k -= gap;
}
a[k+gap] = temp;
}
}
}
3,选择排序:简单选择+堆
//查找最值,和冒泡区别是一个交换出,一个没有交换只是记录,只交换一次;
//下标从1到倒数第二个,因为后面最值取值是从i+1开始
void selectsort(int[] array)
{
for (int i=0;i < array.length -1;i++) {
int mink = i;
for (int j= i+1;j< array.length;j++) {
if (array[j]< array[mink]) {
mink = j;
}
}
if (mink != i) {
swap(&array[i],&array[mink]);
}
}
}
4,堆排序:减少比较次数,找出最大值,堆排序可通过树形结构保存部分比较结果,可减少比较次数
//对n较大的文件还是很有效,其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上
//堆调整是从下到上,然后上面动了,肯定下面也变了,就会包括了下面重新调整的过程
//建堆的过程是必须的,因为这样可以分层,每一层都是一个最值范围,为下面排序只调整根不出现问题
代码:
void heapadjust(int[] array,int parent, int length)
{
int temp = array[parent];
int child = parent * 2 + 1;
//遍历所有子节点
while(child < length) {
//左和右节点选择交换
if (array[child] < array[child+1] && child + 1 < length) {
child ++;
}
//根节点和右节点比较顺序,建堆时候下层已经保证了顺序,所以可以直接退出
if (temp >= array[child]) {
break;
}
//父节点修改值,子节点不必,因为上面的比较还是temp
array[parent] = array[child];
//再去遍历修改过的子节点有无顺序上的问题
parent = child;
child = 2*child +1;
}
//最后的子节点赋值
array[parent] = temp;
}
void heapsort(int[] list)
{
//从非韭页节点开始调整,符合二叉树排序,堆顶是最值
for (int i=list.length/2;i>=0;i--) {
heapadjust(list,i,list.length);
}
for (int i=list.length-1;i>0;i--) {
//堆顶最大值放到最大下标,交换值
int temp = list[i];
list[i] = list[0];
list[0] = temp;
//只调整根节点,因为建堆时候,各层顺序保证正确,不包含最大下标
heapadjust(list,0,i);
}
}
和机器编译链接一样,人也只是把脑子里的思路,变成自然语言,再变各种编程语言
排序是什么?知道学习成绩排名吧,这个过程只有比较和交换
比较不用多说,大于小于判断
交换是什么,两个盒子里的蛋糕1和2,交换一下,把1拿出来放到2,2拿出来放到1
不是的,实际是把1拿出来放桌子上,2拿出来放1,再把1放2,想想过程是否如此
交换函数为啥有个中间变量,这就是那个桌子呀
void swap(int *a, int *b)
{
int temp = 0;
temp = *a;
*a = *b;
*b = temp;
}
排序问题就是解决:军训的时候一般按照个头站队,那么你怎么判断自己站哪?
1,交换排序:冒泡+快速
首先你会挨个看看个头,第一个比我矮,我往后走走,第二个比我高,我站他前面。。。
一次比较后,你会不会站进队伍去呢,你站进去了,再去看后面的个头比你矮,发现不对,
你再出来,是不是多余?
冒泡:相邻比较,符合交换,一次遍历交换出一个最值,如果遍历没有发生交换则已经有序退出
从第一个开始,看后面的人是否比自己高,到最后一个都没有发生交换,说明队伍站齐了
因为第一次没有交换说明是最矮的,后面依次是第二和第三比较,都没有发生交换
交换i从0到len-1,比较从0到len-1-i,因为最值别甩到后面已经确认了i个人的位置
//第一次遍历是n-1次比较,i=0,j<n-1-i,最好是第一次遍历n-1次比较就跳出,O(N),
//最坏就是一直到最后,N-1 + N-2 + ... +1 = O(N^2)
代码:
void BubbleSort(int[] array)
{
bool flag = true;
for (int i = 0; i < array.length - 1; i++) {
flag = false;
for (int j = 0;j < array.length -1 -i;j++) {
if (array[j] < array[j + 1]) {
swap(&array[j], &array[j+1]);
flag = true;
}
}
if (flag == false) {
break;
}
}
}
快速:不找出最值的位置,而是主元的位置,交换是跳跃的不是相邻的
思路类似于教官随便挑一个同学b,比他高的左边,比他矮的右边站
然后b不动,左边随便再找一个同学,右边随便找一个同学重复此行动
代码实现上,集体移动,是有一些存储和算法问题的,所以颠倒顺序
一次移动两个同学,也就是交换操作,左边一个右边一个交换,最后把这个同学的位置确定下
//右边开始查找比主元小的,先找最后相遇会交换,左边找比主元大的,交换,循环,
//右边先找是因为如果左边先找大于主元,最后i=j停下,交换大的在首位,可见ij相遇交换出错乱序
//最优时间复杂度递归深度log2n.+1,辅助空间O(NLogN),最差是斜树和冒泡一致,比较次数N-1 + N-2 + ... +1 = O(N^2)
代码:
void quicksort(int[] array, int left, int right)
{
int i,j,temp;
if (left > right)
return;
temp = array[left];
i = left;
j = right;
while(i!=j) {
while(a[j] >= temp && i < j)
j--;
while(a[i] <= temp && i < j)
i++;
//find i!=j swap i and j
if (i < j) {
swap(&a[i],&a[j]);
}
}
//swap i==j and left
a[left] = a[i];
a[i] = temp;
quicksort(left, i-1);
quicksort(i+1,right);
}
2,插入排序:直接插入+希尔
聪明的人,一般都是放眼望去,就看到自己应该站在哪里,因为他只比较,不交换
从后面开始看,个头比他高,那个人就得后退一格
因为要和前面的比较,i从1开始,j从i-1开始,
//插入,将一个记录插入到已经有序的表中
//插入过程:首先是0~x-1个位置,x准备插入,x之前的值下表一直右移腾出位置
//有比较,没有交换,直接赋值,每次移动一位,最好复杂度为O(N),最差O(N^2)
代码:
void InsertSort(int[] array)
{
int j;
for (int i=1; i<array.length;i++) {
j = i-1;
//插入i元素,从j开始比较,j--
int temp = array[i];
while(j>=0 && temp < array[j]) {
array[j+1] = array[j];
j--;
}
array[j+1] = temp;
}
}
希尔对插入的两点性质:几乎排好序效率高,线性;低效是因为每次只能移动一位
//分组插入,分间隔分而治之不稳定,专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中
//证明它不够快,再改成快速排序这样更高级的排序算法,就是将插入排序的1换成gap
//这样可以快速减少大量的无序情况,从而减轻后续的工作
void shellsort(int a[],int n)
{
int j,gap;
for (gap = n/2;gap > 0; gap /= 2) {
for (j = gap;j < n; j++) {
int k = j-gap;
int temp = a[j];
while (k >= 0 && a[k] > temp) {
a[k+gap] = a[k];
k -= gap;
}
a[k+gap] = temp;
}
}
}
3,选择排序:简单选择+堆
//查找最值,和冒泡区别是一个交换出,一个没有交换只是记录,只交换一次;
//下标从1到倒数第二个,因为后面最值取值是从i+1开始
void selectsort(int[] array)
{
for (int i=0;i < array.length -1;i++) {
int mink = i;
for (int j= i+1;j< array.length;j++) {
if (array[j]< array[mink]) {
mink = j;
}
}
if (mink != i) {
swap(&array[i],&array[mink]);
}
}
}
4,堆排序:减少比较次数,找出最大值,堆排序可通过树形结构保存部分比较结果,可减少比较次数
//对n较大的文件还是很有效,其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上
//堆调整是从下到上,然后上面动了,肯定下面也变了,就会包括了下面重新调整的过程
//建堆的过程是必须的,因为这样可以分层,每一层都是一个最值范围,为下面排序只调整根不出现问题
代码:
void heapadjust(int[] array,int parent, int length)
{
int temp = array[parent];
int child = parent * 2 + 1;
//遍历所有子节点
while(child < length) {
//左和右节点选择交换
if (array[child] < array[child+1] && child + 1 < length) {
child ++;
}
//根节点和右节点比较顺序,建堆时候下层已经保证了顺序,所以可以直接退出
if (temp >= array[child]) {
break;
}
//父节点修改值,子节点不必,因为上面的比较还是temp
array[parent] = array[child];
//再去遍历修改过的子节点有无顺序上的问题
parent = child;
child = 2*child +1;
}
//最后的子节点赋值
array[parent] = temp;
}
void heapsort(int[] list)
{
//从非韭页节点开始调整,符合二叉树排序,堆顶是最值
for (int i=list.length/2;i>=0;i--) {
heapadjust(list,i,list.length);
}
for (int i=list.length-1;i>0;i--) {
//堆顶最大值放到最大下标,交换值
int temp = list[i];
list[i] = list[0];
list[0] = temp;
//只调整根节点,因为建堆时候,各层顺序保证正确,不包含最大下标
heapadjust(list,0,i);
}
}