排序
1.问题引出
给定一个整数数组,按从小到大的数组将其进行排序;
2.冒泡排序
2.1 思路
冒泡排序应该是最简单的一种排序算法了,其特定是是一种稳定的排序算法;
一般情况下,称某个排序算法稳定,指的是当待排序序列中有相同的元素时,它们的相对位置在排序前后不会发生改变。
假设待排序序列为 (5,1,4,2,8),如果采用冒泡排序对其进行升序(由小到大)排序,则整个排序过程如下所示:
-
第一轮排序,此时整个序列中的元素都位于待排序序列,依次扫描每对相邻的元素,并对顺序不正确的元素对交换位置,整个过程下图 所示。
-
第二轮排序,此时待排序序列只包含前 4 个元素,依次扫描每对相邻元素,对顺序不正确的元素对交换位置,整个过程如下图所示。
后面的过程同理。
2.2 实现
//冒泡的关键在于for循环的截至条件;
public class BubbleSort{
public void bubbleSort(int[] arr){
int l = arr.length;
for(int i = 0; i < l; i++){ //比较的轮数
for(int j = 0; j < l-i-1; j++){ //每一轮比较几次;后面排好的几个就不用比了;
if(arr[j] > arr[j+1]){ //交换前后元素;
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
时间复杂度:O(N^N);
3. 快速排序
3.1 思路
分而治之
比如说我们要排序:“6 1 2 7 9 3 4 5 10 8”;
1.从序列两端开始探测,先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。
2.接着上述步骤,直到两个人遇上了,说明此次探测结束;将遇上的元素和6交换;
注意 这时候相遇的一定是比6小的,原因:因为每次都是先从右往左走,也就是每次都是哨兵j先动,j先停,所以只要它停下来一定是比6小的,所以最后可以放心交换6与相遇的位置;
经过第一轮探测后原序列以6为分界点,左边都比6小,右边都比6大;这个6已经到了自己正确的位置上,归位完毕;
3.分别按照上述思路处理左右两个序列;
快速排序之所比较快,因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。
3.2 实现
public class QuickSort{
public void quickSort(int[] arr, int head; int tail){
if(tail < head) return; //设置递归终止条件;也就是只有两个元素的时候,j和i相遇就是在i处,传进j-1就变成了小于head的;
int i = head, j = tail;
int temp = arr[head]; //基准位;
while(i < j){
//一定要从右边开始,目的其实是为了让停下来的元素是小于基准元素的,
//然后再互换的时候,保证换完之后基准左边的都是小于基准的;
while(i < j && arr[j] >= temp){
j--;
}
while(i < j && arr[i] < temp){
i++;
}
if(i < j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//到这里一轮结束,交换相遇的和基准位的;
arr[head] = arr[j];
arr[j] = temp; //基准位归位;
quickSort(arr, head, j-1); //快排左边序列;
quickSort(arr,j+1,tail); //快排右边序列;
}
}