漫步IOS--部分排序一:冒泡排序、选择排序和快速排序

来源:互联网 发布:java timestamp 格式 编辑:程序博客网 时间:2024/05/16 03:12

1 冒泡排序

冒泡排序的思路:从序列的一头开始,每次比较相邻的两个数,将大的往后移.当走到序列结尾的时候,最后一个即是最大的;然后再从头开始比较,只是这次比较到倒数第二个就好,因为最后一个已经确定是最大了。如此往复,直到没有需要比较的元素为止。个人表述有限,可参考维基百科介绍。

参考代码:

//冒泡排序void sortUp(int arr[],int size){    for (int i = 0; i < size ; i++) {        for (int j = size-1; j > i; j--) {            if(arr[i] > arr[j]){                arr[i] += arr[j];                arr[j] = arr[i] - arr[j];                arr[i] = arr[i] - arr[j];            }        }    }}


时间复杂度:

冒泡排序一般是作为程序员入门算法进行讨论的,因为他的效率着实有些低。别的不说,相比选择排序就落好几条街。

最好时间复杂度是O(n);

这种情况是:  6 , 1, 2, 3, 4, 5,  类似吧。即只需一趟即可把顺序完全排好。出现的概率有点儿低。

最坏时间复杂度是O(n2);

这种情况是:6,5,4,3,2,1类似吧,即每一趟的每一次比较都需要进行数据交换。对此不予评论。

2 选择排序

选择排序的思路:每一趟扫描选取剩下的元素中一个最大的放在后边。他与冒泡排序的区别在于,在扫描的过程中,只是保留选中元素的下标,而并不是将元素进行位移。更换选中的数据也只是更换一下下标的值,中间不会产生数据移动。所以在效率上会高很多很多。

参考代码

//选择排序void sortSelect(int arr[],int size){    int re[size];    for (int i = 0; i < size; i++) {        int min = i;        for (int j = i+1; j < size; j++) {            if (arr[min] > arr[j]) {                min = j;            }        }        if(min != i){            arr[i] += arr[j];            arr[j] = arr[i] - arr[j];            arr[i] = arr[i] - arr[j];        }    }}


时间复杂度:

比较次数O(n2),交换次数O(n),因为在计算机中交换的CPU时间需要的比比较所需要的CPU时间要长的多,所以选择排序比冒泡排序要快许多。最好情况是,已经有序,交换0次;最坏情况逆序交换n-1


3 快速排序

快速排序的思路:在序列中选择一个标杆,通常选择序列的第一个数,然后对序列中的数从两头开始,将小于他的数放在左边,大于他的数放在他右边。这时以这个数为标杆,分成了左右两块,左边的都小于他,右边的都大于他。然后再递归调用,直到没有可排序的。即只有两个元素。感觉不大容易看懂,所以贴了一个动图来看看。


算法思路:我贴一下百度的讲解吧。

假设用户输入了如下数组:
下标
0
1
2
3
4
5
数据
6
2
7
3
8
9
创建变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(赋值为第一个数据的值)。
我们取走了下标0的数据,于是,我们需要找到一个数字来替换他。由于我们要把所有比6小的数移动到左面,所以我们可以开始寻找比6小的数并从右往左找。别急,我们要按顺序找哦。不断递减j的值,我们发现下标3的数据比6小,于是把3移到下标0(实际是i指向的位置。代码中要用i,因为后面还会循环这个步骤,不用i的话第二次循环:
下标
0
1
2
3
5
5
数据
3
2
7
3
8
9
i=0 j=3 k=6
由于变量k已经储存了下标0的数据,所以我们可以放心的把下标0覆盖了。如此一来,下标3虽然有数据,但是相当于没有了,因为数据已经复制到别的地方了。于是我们再找一个数据来替换他。这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2是第一个比k大的,于是用下标2的数据7替换j指向的下标3的数据,数据状态变成下表:
下标
0
1
2
3
4
5
数据
3
2
7
7
8
9
i=2 j=3 k=6
重复上面的步骤,递减变量j。这时,我们发现i和j“碰头”了:他们都指向了下标2。于是,循环结束,把k填回下标2里,即得到结果。
填回k之后状态为:
下标
0
1
2
3
4
5
数据
3
2
6
7
8
9
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
注意:快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。(你可以想象一下i和j是两个机器人,数据就是大小不一的石头,先取走i前面的石头留出回旋的空间,然后他们轮流分别挑选比k大和比k小的石头扔给对面,最后在他们中间把取走的那块石头放回去,于是比这块石头大的全扔给了j那一边,小的全扔给了i那一边。只是这次运气好,扔完一次刚好排整齐。)为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。
参考代码:
//快速排序void sortQuickly(int arr[],int low,int high){    //m代表前面的标记,n代表后面的标记。L表示当前的中间位    int m = low,n = high;    int temp = arr[low];    if (low < high) {        while(m < n){            while((arr[n] >= temp) &&  (m < n)){                n--;            }            arr[m] = arr[n];            while((arr[m] <= temp) &&  (m < n)){                m++;            }            arr[n] = arr[m];        }        arr[n] = temp;        sortQuickly(arr,low,m-1);        sortQuickly(arr,n+1,high);    }else{        return;    }}

时间复杂度:
快速排序是面试题中经常会提到的排序算法之一。也是最容易让人产生忘性的,建议多敲几遍加强一下记忆。
最差时间复杂度:O(n2)
最好时间复杂度:O(nlogn)
今天写的匆忙,后面会再修改更新。

0 0
原创粉丝点击