排序
-
定义
排序是计算机程序设计中一种重要操作,它的功能是将一个数据元素(或记录)的任意序列重新组合成一个按关键字有序的序列。 -
排序分类
按待排序的数量不同,使得排序的过程中涉及的存储器不同可分为:
(1)内部排序:排序数据元素在内存上进行操作排序的过程
(2)外部排序:由于待排序的数据元素量很大,内存上不能容纳全部元素,需要在外存进行访问排序的过程。
我们所常涉及的内部排序算法进行归纳:
基数排序这里不提及但它也属于内部排序
下面我们对以上几种算法进行简单的阐述:
(1)冒泡排序
核心思想:
元素两两之间进行比较,后边大于前边进行交换,否则继续向后遍历,遍历完一趟就能最大元素交换到最后,再将前n-1个元素重新比较,直到循环遍历完即可得到有序序列
第一趟走完继续上述操作,直到循环结束,排序结束。
未优化的代码实现public static void bubbleSort(int[]arr) { int temp = 0; for (int i = 0; i < arr.length-1; i++) { for (int j = 0; j < arr.length-1-i; j++) { if (arr[j] >arr[j+1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } }
对于已经有序的序列按上边的方法会很浪费时间,我们加上一个判断,如果序列本身有序则就直接返回,不进循环:
public static void bubbleSort(int[]arr) {
int temp = 0;
for (int i = 0; i < arr.length-1; i++) {
boolean swap=false;
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] >arr[j+1]) {
swap=true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!swap){//未进行交换
return;
}
}
}
(2)选择排序
核心思想:
在序列里先找到最小的元素,将最小的元素放到与第一个元素进行交换,将其后的n-1个元素中找到最小元素与第二个进行交换,依次进行下去,直到找到有序序列
代码实现:
public static void choiceSort(int[]arr){
for (int i=0;i<arr.length-1;i++){
int k=i;//最小值下标
int min=arr[i];
//通过循环来比较最小的比当前min小的值,通过不断更新求出min,
//最好的情况是序列已经排好,只需要比较n次
for (int j=i+1;j<arr.length;j++) {
if (arr[j] < min) {//za
min = arr[j];
k=j;
}
}
插入排序:
往一个有序序列后插入元素,在这个有序序列中找到第一个比它小的元素将它插到比它小的元素后边,继续该操作(涉及数据移动)
void InsertSort(int arr[], int length)
{
for (int i = 1; i < length; i++)
{
int j;
if (arr[i] < arr[i - 1])
{
int temp = arr[i];
for (j = i - 1; j >= 0 && temp < arr[j]; j--)
{
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
}
}
由于插入排序具有部分是顺序的特点,我们可以优化该算法
//二分查找确定元素位置,不用一一比较
代码如下:
二分查找函数:
private int binarySearch(int[] arr, int first,
int last, int val) {
while(first <= last){
int mid = (first + last)/2;
if(val < arr[mid]){
last = mid-1;
} else if(val > arr[mid]){
first = mid+1;
} else {
return mid;
}
}
return -1;
}
插入函数:
private static void insertSort(int[] arr) {
// 12 12 7 8 9
for(int i=1; i<arr.length; ++i) {
int val = arr[i]
// 优化成二分查找合适的插入位置,不再使用线性查找
// 0 j 找第一个小于val的值
int index = findInsertPos(arr, 0, i-1, val);
for(int j=i; j>index; --j){
arr[j] = arr[j-1];
}
arr[index] = val;
/*
// 在i前面已经排序好的序列中,找i元素插入的合适的位置
int j=i-1;
for(; j>=0; --j){
if(val < arr[j]) {
arr[j+1] = arr[j];
}
else {
break;
}
}
// 把val元素插入到当前位置,然后循环结束
arr[j+1] = val;
*/
}
}
(3)快速排序
核心思想:设置基准元素,设置j和i变量j从后向前遍历找到比基准数小且i从基准数之后遍历且比基准数大,此时i号元素与j号元素进行交换。直到i=j时,将基准数与i号元素进行交换。此时基准数将序列分成两部分,我们先将左边的序列依照上述方法进行比较,再将右边序列同样进行比较。循环上述操作。最总形成有序数列。
(1)8作为基准元,j从后往前遍历j号元素<基准元素,同时i从基准元的后一个元素开始后遍历,i号元素>基准元,i号元素与j号元进行交换。
交换之后的序列:8 5 3 4 3 9
(2)i++,j–,此时相遇(i=j),已找到基准元的位置。将基准元放入i号位置
(3)将6左右两边分别进行(1)(2)操作。
具体步骤可参考下图:
代码实现:
public class 快速排序{
public void tes t05(){
int[] arr = new int[20];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*1000);
}
int data = select_no_k_value(arr, 0, arr.length-1, 5);
System.out.println("data:" + data);
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
@Test
public void test04(){
int[] arr = new int[20];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*100);
}
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
private void quickSort(int[] arr, int i, int j) {
if(i > j)
return;
/*if(j - i <= 20){
insertSort(arr, i, j);
return;
}*/
int l = partation(arr, i, j); // left == right
quickSort(arr, i, l-1);
quickSort(arr, l+1, j);
}
private int partation(int[] arr, int l, int r) { // O(log2n)
int val = arr[l];
while(l < r){
// 1. 从r开始往l的方向找第一个小于val的数字
while(l < r && arr[r] > val){
r--;
}
// 2. 把第一个小于val的元素值写入l里面,并且l++
if(l < r){
arr[l++] = arr[r];
}
// 3. 从l往r的方向找第一个大于val的数字
while(l < r && arr[l] < val){
l++;
}
// 4. 把第一个大于val的元素值写入r里面,并且r--
if(l < r){
arr[r--] = arr[l];
}
}
arr[l] = val;
return l;
}
归并排和堆排下次更新!