冒泡排序(Bubble Sort)
-思想
每两个数比较大小,大的数下沉,小的数上升。
-过程
(1)从前向后,逐位取相邻两个元素,arr[i]和arr[i+1]。
(2)如果arr[i] > arr[i+1],那么交换位置,否则,重新取新的两位元素;
-图示
图片来源:http://www.cnblogs.com/ysocean/p/7896269.html#_label2,侵删
-java代码
public class BubbleSort {
public static void bubbleSort(int[] unsorted) {
int uslen = unsorted.length;
int temp = 0;
for(int i = 0; i < uslen - 1; i++) {//表示趟数,也可以理解为需要为uslen个位置确定值
for(int j = 1; j< uslen -i;j++) {//在每一趟中,从前向后,选择相邻的两个元素比较大小,大的在后,小的在前
if(unsorted[j] < unsorted[j -1]) {
temp = unsorted[j];
unsorted[j] = unsorted[j-1];
unsorted[j-1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] testunsorteday = {1,23,45,21,567,34,45,1,3,78};
bubbleSort(testunsorteday);
for(int ele : testunsorteday) {
System.out.print(ele +" ");
}
}
}
-时间复杂度:O(n2)
-算法优化
针对问题:如果在其中的某一轮操作后,数组已经是有序的,那么之后的比较过程不需要进行
改进方法:加入标志信息flag,初始值为false,如果在某一趟比较中,发现并没有发生交换操作,那么表示数组已经排好序,flag = true。
java代码
public class BubbleSort {
public static void bubbleSort(int[] unsorted) {
int uslen = unsorted.length;
int temp = 0;
boolean flag = false;
for(int i = 0; i < uslen -1 ; i++) {//表示趟数,也可以理解为需要为uslen个位置确定值
flag = false;
for(int j = 1; j< uslen -i;j++) {//在每一趟中,从前向后,选择相邻的两个元素比较大小,大的在后,小的在前
if(unsorted[j] < unsorted[j -1]) {
temp = unsorted[j];
unsorted[j] = unsorted[j-1];
unsorted[j-1] = temp;
flag = true;
}
}
if(flag != true) {
break;
}
}
}
public static void main(String[] args) {
int[] testunsorteday = {1,23,45,45,78,89,100};
bubbleSort(testunsorteday);
for(int ele : testunsorteday) {
System.out.print(ele +" ");
}
}
}
插入排序(Insertion Sort)
-思想
将待排序的元素插入到已排好序的子数组中。
-过程
(1)位置i的元素arr[i],那么i之前的元素已经排好序了;
(2)arr[i]和前面的元素逐个比较,找到自己的新位置后插入,由于会占用原来元素的位置,所以该位置之后的元素均向后移动。
-图示
图片来源:http://www.runoob.com/w3cnote/sort-algorithm-summary.html,侵删
-java实现
public class InsertSort {
public static void insertSort(int[] unsorted) {
int uslen = unsorted.length;
int temp;
for(int i = 1; i<uslen;i++) {//待排序的元素位置
if(unsorted[i] < unsorted[i-1]) {
temp = unsorted[i];
int j = i -1;
while(j >= 0 && temp < unsorted[j]) {//待排序的元素和前面的元素逐个比较大小
unsorted[j+1] = unsorted[j];
j--;
}
unsorted[j+1] = temp;
}
}
}
public static void main(String[] args) {
int[] testarr = {45,23,56,1,4,67,88,345,43};
insertSort(testarr);
for(int ele : testarr) {
System.out.print(ele + " ");
}
}
}
-时间复杂度:O(n2)
希尔排序(Shell Sort)
-思路
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。希尔排序是记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
我们分割待排序记录的目的是减少待排序记录的个数,并使整个序列向基本有序发展。而如上面这样分完组后,就各自排序的方法达不到我们的要求。因此,我们需要采取跳跃分割的策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
-过程
初始时,有一个大小为 10 的无序序列。
(1)在第一趟排序中,我们不妨设 gap1 = N / 2 = 5,即相隔距离为 5 的元素组成一组,可以分为 5 组。
(2)接下来,按照直接插入排序的方法对每个组进行排序。
在第二趟排序中,我们把上次的 gap 缩小一半,即 gap2 = gap1 / 2 = 2 (取整数)。这样每相隔距离为 2 的元素组成一组,可以分为 2 组。
(3)按照直接插入排序的方法对每个组进行排序。
(4)在第三趟排序中,再次把 gap 缩小一半,即gap3 = gap2 / 2 = 1。 这样相隔距离为 1 的元素组成一组,即只有一组。
(5)按照直接插入排序的方法对每个组进行排序。此时,排序已经结束。
-java实现
package sortalg;
public class ShellSort {
public static void shellSort(int[] unsorted) {
int uslen = unsorted.length;
int hip = uslen/2;
while(hip > 0) {
for(int i = 0;i<hip;i++) {
for(int j =i+hip; j<uslen;j+=hip) {
int m = j;
int base = unsorted[j];
while((m -= hip)>=0 && base <= unsorted[m]) {
unsorted[m+hip] = unsorted[m];
}
unsorted[m+hip] = base;
}
}
hip /= 2;
}
}
public static void main(String[] args) {
int[] testarr = {1,4,3,7,4,0,5,8,3,9,6};
shellSort(testarr);
for(int ele : testarr) {
System.out.print(ele + " ");
}
}
}
-时间复杂度O(nlogn)(此处原理还没解决??)
-参考资料
选择排序
-思路
找i位置后所有元素的最小值与位置i上的数比较,较小的值留在i位置。
-过程
0位置上元素的确定:寻找[1,length-1]中最小的元素,然后和0位上的值比较,如果最小的元素小于0位上的值,那么互换。
1位置上元素的确定:寻找[2,length-1]中最小的元素,然后和1位上的值比较,如果最小的元素小于1位上的值,那么互换。
….
length -2位置上元素的确定:寻找寻找[length – 1,length-1]中最小的元素,然后和(length -2)位上的值比较,如果最小的元素小于(length -2)位上的值,那么互换。
-java代码
public class SelectionSort {
public static void selectionSort(int[] unsorted) {
int uslen = unsorted.length;
int mindex = 0;
int temp =0;
for(int i = 0; i< uslen - 1;i++) {
mindex = i+1;
for(int j = i+1; j < uslen;j++) {
if(unsorted[j] < unsorted[mindex] ) {
mindex = j;
}
}
if(unsorted[i] > unsorted[mindex]) {
temp = unsorted[i];
unsorted[i] = unsorted[mindex];
unsorted[mindex] = temp;
}
}
}
public static void main(String[] args) {
int[] testarray = {1,34,23,12,56,35,789,3};
selectionSort(testarray);
for(int ele : testarray) {
System.out.print(ele + " ");
}
}
}
-时间复杂度:O(n2)
快速排序(Quick Sort)
-思路
快速排序使用分治思想实现,即选择一个比较基准,经过一趟比较后,小于基准的元素位于基准的左边,大于基准的元素位于基准的右边。然后对基准左右两边的子数组再分别进行排序。
-过程
(1)选择基准base,一般选择第一个元素值作为基准。low = 0,high = length-1;
(2)从后向前,取每个元素和base比较(即high–),当元素值<base时,将元素值和base位置互换,此时high位置上的值为base;
(3)从前向后,low++,取每个元素和base比较,当元素值>base时,将元素值和base互换,此时low位置上的值为base;
(4)重复(2)(3)步骤,直到low==high。
-图示
图片来源:https://blog.csdn.net/u010853261/article/details/54884784,侵删
-java实现
public class QuickSort {
public static void quickSort(int[] unsorted,int low,int high) {
int bsidx;
if(high -low < 1) {//high- low = 0 :子数组中只有一个元素; high -low = -1:子数组中没有元素
return;
}
bsidx = partition(unsorted,low,high);
quickSort(unsorted,low,bsidx -1);
quickSort(unsorted,bsidx+1,high);
}
//找到基准在有序结果中的位置
public static int partition(int[] unsorted,int low, int high) {
int base = unsorted[low];
while(low < high) {
//从后面向前遍历的元素,都是大于基准值的 ,那么high--
//有一种情况:low位置后的元素均大于基准值,那么high>low保证了base不会和基准值之前的元素做比较了
//high > low保证了经过自减操作的high,不会超出数组下界
while(base <= unsorted[high] && high > low) {
high--;
}
unsorted[low] = unsorted[high];
//从前向后遍历的元素,都是小于基准值的 ,那么low++
//有一种情况:high位置前的元素均小于基准值,那么high>low保证了base不会和基准值之后的元素做比较了
//high > low保证了经过自加操作的low,不会超出数组上界
while(base >= unsorted[low] && high > low) {
low++;
}
unsorted[high] = unsorted[low];
}
unsorted[high] = base;
return high;
}
//测试
public static void main(String[] args) {
int[] testarr = {12,3,567,89,34,65,86,2,9,46,58};
quickSort(testarr,0,testarr.length - 1);
for(int ele : testarr) {
System.out.print(ele + " ");
}
}
}
-时间复杂度O(nlogn)
-参考链接
归并排序(Merge Sort)
-前言
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。
首先考虑下如何将2个有序数列合并。这个非常简单,只要从比较2个数列的第一个数开始,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
//将有序数组a[]和b[]合并到c[]中
void MemeryArray(int a[], int n, int b[], int m, int c[])
{
int i, j, k;
i = j = k = 0;
while (i < n && j < m)
{
if (a[i] < b[j])
c[k++] = a[i++];
else
c[k++] = b[j++];
}
while (i < n)
c[k++] = a[i++];
while (j < m)
c[k++] = b[j++];
}
-思路
归并排序算法,引用了归并的思想,基本思想是:将数组分成2组A,B,如果这2组组内的数据都是有序的,那么就可以很方便的将这2组数据进行排序。如何让这2组组内数据有序了?
可以将A,B组各自再分成2组。依次类推,当分出来的小组只有1个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的2个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。
-过程
-java实现
public class MergeSort {
public static void mergesort(int[] unsorted,int start,int end,int[] temp) {
if(start < end) {
int middle = (start + end)/2;
mergesort(unsorted,start,middle,temp);
mergesort(unsorted,middle+1,end,temp);
mergearray(unsorted,start,middle,end,temp);
}
}
//将排好序的子数组[start,middle]和[middle+1,end]合并为一个有序的数组
public static void mergearray(int[] unsorted,int start,int middle,int end,int[] temp) {
int i = start;
int j = middle+1;
int k = 0;
while(i <= middle && j <= end) {
if (unsorted[i] < unsorted[j]) {
temp[k++] = unsorted[i++];
} else {
temp[k++] = unsorted[j++];
}
}
if(i <= middle) {
for(;i<=middle;i++) {
temp[k++] = unsorted[i];
}
}
if(j <= end) {
for(;j<=end;j++) {
temp[k++] = unsorted[j];
}
}
for(int m =0,l=start;m<= end - start;m++) {//temp是每次存储有序子数组的,其大小虽然为unsorted.length,但是其中有效的只有前end-start+1位。
unsorted[l++] = temp[m];
}
}
public static void main(String[] args) {
int[] testarr = {12,34,2,56,678,45,65,7,9,87};
int[] temp = new int[testarr.length];
mergesort(testarr,0,testarr.length -1,temp);
for(int ele : testarr) {
System.out.print(ele +" ");
}
}
}
-时间复杂度O(nlogn)
基数排序()
-BinSort
基本思想:
BinSort想法非常简单,首先创建数组A[MaxValue](MaxValue为原数组中最大的元素值);然后将每个数放到相应的位置上(例如17放在下标17的数组位置);最后遍历数组,即为排序后的结果。图示:
BinSort
- 问题: 当序列中存在较大值时,BinSort 的排序方法会浪费大量的空间开销。
RadixSort
- 基本思想: 基数排序是在BinSort的基础上,通过基数的限制来减少空间的开销。
- 过程:
过程1
过程2
(1)首先确定基数为10,数组的长度也就是10。每个数(比如34)都会在这10个数中寻找自己的位置。
(2)不同于BinSort会直接将数34放在数组的下标34处,基数排序是将34分开为3和4,第一轮排序根据最末位放在数组的下标4处,第二轮排序根据倒数第二位放在数组的下标3处,然后遍历数组即可。
public static void RadixSort(int A[],int temp[],int n,int k,int r,int cnt[]){
//A:原数组
//temp:临时数组
//n:序列的数字个数
//k:最大的位数2
//r:基数10
//cnt:存储bin[i]的个数
for(int i=0 , rtok=1; i<k ; i++ ,rtok = rtok*r){
//初始化
for(int j=0;j<r;j++){
cnt[j] = 0;
}
//计算每个箱子的数字个数
for(int j=0;j<n;j++){
cnt[(A[j]/rtok)%r]++;
}
//cnt[j]的个数修改为前j个箱子一共有几个数字
for(int j=1;j<r;j++){
cnt[j] = cnt[j-1] + cnt[j];
}
for(int j = n-1;j>=0;j--){ //重点理解
cnt[(A[j]/rtok)%r]--;
temp[cnt[(A[j]/rtok)%r]] = A[j];
}
for(int j=0;j<n;j++){
A[j] = temp[j];
}
}
}
-参考链接
堆排序(Heap Sort)
-关于堆数据结构的基础知识,可以参考博客资源:堆数据结构
-堆排序的实现:
1.利用BUILD-MAX-HEAP将输入数组A[1..n]建成最大堆,n=A.length,h = A.heapsize=A.length;
2.在生成的最大堆中,根结点即A[1]存放的是最大的元素,那么可以将A[1]和A[h]互换,使A[h]的值为最大值;
3.在第二步的基础上,可以去掉结点h(通过减少A.heapsize的值来实现,即h–)。在剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根结点可能会违背最大堆的性质。
4.为了维护最大堆的性质,调用MAX-HEAPIFY(A,1),从而在A[1,h]上构造一个新的最大值。
5,不断重复以上2-4步,直到h==1的时候。
-图例
给定数组内容为:16,14,10,8,7,9,3,2,4,1
-时间复杂度:O(nlgn)
-java实现
package sortalg;
public class HeapSort {
/*
* 功能:建堆,数组中的元素满足最大堆性质
*/
public void buildMaxHeap(int[] arr) {
int len = arr.length;
for(int i = len/2 - 1 ;i >= 0; i--) {
maxHeapify(arr,i,true,0);
}
}
/*
* 功能:维护最大堆性质
*/
public void maxHeapify(int[] arr,int index,boolean flag,int heapsize) {
int largest = index;
int temp;
int arrange;
if(flag == true) {//表示数组中所有的元素都是可操作的堆元素
arrange = arr.length - 1;
}else {//表示数组中只有[0,heapsize]中的元素是可操作的堆元素
arrange = heapsize;
}
if(index * 2 +1 <= arrange && arr[index] < arr[index *2 +1]) {
largest = index * 2 +1;
}
if(index * 2 +2 <= arrange && arr[largest] < arr[index * 2 +2]) {
largest = index *2 +2;
}
if(largest != index) {
temp = arr[index];
arr[index] = arr[largest];
arr[largest] = temp;
maxHeapify(arr,largest,flag,heapsize);
}
}
/*
* 功能:堆排序实现
*/
public void heapSort(int[] arr) {
int hsize = arr.length - 1;
int temp;
while(hsize > 0) {
temp = arr[hsize];
arr[hsize] = arr[0];
arr[0] = temp;
hsize--;
maxHeapify(arr,0,false,hsize);
}
}
/*
* 测试
*/
public static void main(String[] args) {
HeapSort hs = new HeapSort();
int[] test = {16,14,10,8,7,9,3,2,4,1};
hs.buildMaxHeap(test);
for(int i:test) {
System.out.print(i+ " ");
}
System.out.println();
hs.heapSort(test);
for(int i:test) {
System.out.print(i+ " ");
}
}
}