1排序算法
1.0排序算法说明
排序的定义
对一序列对象根据某个关键字进行排序
术语说明
- 稳定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
- 不稳定 :如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后- 面;
- 内排序 :所有排序操作都在内存中完成;
- 外排序 :由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
- 时间复杂度 : 一个算法执行所耗费的时间。
- 空间复杂度 :运行完一个程序所需内存的大小。
各种算法的通俗理解!
贪心算法:
从左到右,两两互换,每一次最后一个数总是最大的
选择排序:
从左到右,找到从当前位置开始最小的那个数,然后将当前位置的数与那个最小的数位置互换,直到最后
直接插入算法:
从左到右,该数左边的序列已经是有序的,如果该数比左边第一个数小,那么从左边第一个数开始,若前一个数还是比该数小,继续往后挪一个,否则就可以插入了
希尔排序:
希尔排序是基于插入排序的升级版,通过分组的不断扩大(这个过程由增量的不断减小来实现),使得每一次调整过后当前数组都比原来处于一种更有序的状态,其调整的增量能够保证最后一次进行插入排序时序列中大部分的元素处于有序状态。
快速排序:
快速排序是将数组一分为二,一般以第一个元素作为分界点,从右到左找比分界点小的数,将其与分界点互换位置,从左到右找比分界点大的数,将其与分界点互换,然后将分界点放在分界的位置上,并分别对左边和右边的子数组进行递归快排
归并排序:
首先按照数组长度将数组一分为二,一直分,分到只有一个数的时候,然后开始归并这些数,按我的理解就是一个打散再重建的过程,但是在重建的过程中会有排序的合并操作,归并的时候定义一个能够容纳两左右数组的大数组用于返回值,考虑四种情况,第一,左边没有,返回右边数组;第二,右边没有,返回左边数组;第三,左边的数组值大于右边的数组值,将右边的值放入大数组,右边索引加一;第四,右边的数组值大于左边的数组值,将左边的值放入大数组,左边索引加一。递归返回大数组。
算法总结
1.1冒泡排序(标志位优化版)
冒泡排序的原理:
比较两个相邻的元素,将值大的元素交换至右端。
思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
设立标志位优化
有一个小小的优化:如果在某一趟的比较中没有发生任何交换,那么说明数组中每一个左边的数都比右边的数小,已经形成了事实上的有序,这时便不需要再进行下一趟的比较辣!!因此设置一个标志位,将其作为是否进行下一趟比较的判断条件之一,在每一趟开始之前将其置为false,如果发生了交换就将其置为true,以便进行下一次的循环,否则没发生交换标志位一直为false,下一趟也就不用进行啦~
一定要注意循环条件的边界啊喂!!!!!
代码:
//冒泡算法(设置了标志位的改进版)
public static int[] maoPao (int [] array) {
if (array.length == 0) {
return array;
}
boolean flag = true;
for (int i=0;i<array.length&&flag;i++) {
for (int j=0;j<array.length-1-i;j++) { //注意i和j的边界
flag = false;
if (array[j]>array[j+1]) {
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true;
}
}
}
return array;
}
1.2选择排序
选择排序的原理:
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
代码
public static int[] xuanZe(int[] array) {
if (array==null) {
return null;
}
if (array.length<2) {
return array;
}
for (int i=0;i<array.length-1;i++) {
int temp = i;
for (int j=i+1;j<array.length;j++) {
if (array[temp]>array[j]) {
temp = j;
}
}
swap(array,i,temp);
}
return array;
}
1.3插入排序
插入排序的原理:
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。
代码:
public static int[] chaRu(int[] array) {
if (array==null) {
return null;
}
if (array.length<2) {
return array;
}
int now;
for (int i=0;i<array.length-1;i++) {
now = array[i+1];
int preIndex = i;
while (preIndex>=0&&array[preIndex]>now) {
array[preIndex+1] = array[preIndex];
preIndex--;
}
array[preIndex+1] = now;
}
return array;
}
1.4希尔排序
希尔排序的原理:
希尔算法又名缩小增量排序,本质是插入排序,只不过是将待排序的序列按某种规则分成几个子序列,分别对几个子序列进行直接插入排序。这个规则就是增量,增量选取很重要,增量一般选序列长度一半,然后逐半递减,直到最后一个增量为1,为1相当于直接插入排序。
public static int[] xiEr (int[] array) {
if (array==null) {
return null;
}
if (array.length<2) {
return array;
}
int gap = array.length/2;
while (gap>0) {
for (int i=0;i<array.length-gap;i++) {
int now = array[i+gap];
int preIndex = i;
while (preIndex>=0&&array[preIndex]>now) {
array[preIndex+gap] = array[preIndex];
preIndex -= gap;
}
array[preIndex+gap] = now;
}
gap = gap/2;
}
return array;
}
1.5归并排序
归并排序的原理:
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
代码:
public static void guiBing(int[] R,int low,int high){
if(low<high){//区间长度大于1
int m=(low+high)/2;
guiBing(R,low,m);
guiBing(R,m+1,high);
merge(R,R.length,low,m,high);
}
}
public static void merge(int[] R,int tempSize,int low,int m,int high){
int i=low;
int j=m+1;
int p=0;
int[] temp=new int[tempSize];//动态创建临时的空间
while(i<=m&&j<=high){
temp[p++]=(R[i]<R[j])?R[i++]:R[j++];//把小的放到temp中
}
while(i<=m){
temp[p++]=R[i++];//把剩余的元素放到临时数组中
}
while(j<=high){
temp[p++]=R[j++];//把剩余的元素放到临时数组中
}
for(int z=0,x=low;x<=high;x++,z++){
R[x]=temp[z];//把数组赋值给数组
}
}
1.6快速排序
快速排序的原理:
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据(基准值),然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
快速排序中基准值的选取(三种方法):
- 常用:
通常的、没有经过充分考虑的选择是将第一个元素做为"基准“。如果输入书随机的,那么这是可以接受的,但是如果输入是预排序的或是反序的,那么这样的”基准“就是一个劣质的分割,因为所以的元素不是被划入S1就是被划入S2。实际上,如果第一个元素用作”基准“而且输入是预先排序的,那么快速排序花费的时间将是二次的,可是实际上却没干什么事,因此,使用第一个元素作为”基准“是绝对糟糕的。 - 一种安全的方法:
一种安全的方法是随机选取”基准“。这种策略是非常安全的,除非随机生成器有问题,但是随机数的生成一般是昂贵的,减少不了算法其余部分的平均运行时间。 - 三数取中法:
一组N个数的中值是第[N/2]个最大的数。”基准“的最好选择是数组的中值。但是这很难算出,且减慢快速排序的速度。这样的中值的估计量可以通过随机选取三个元素并用它们的中值作为”基准”而得到。实际上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为“基准”。
三数取中法的实现代码:
ElementType Median3(ElementType A[],int Left,int Right)
{
int Center = (Left + Right) / 2;
if(A[Left] > A[Center])
Swap(&A[Left],&A[Center]);
if(A[Left] > A[Right])
Swap(&A[Left],&A[Right]);
if(A[Center] > A[Right])
Swap(&A[Center],&A[Right]);
Swap(&A[Center],&A[Right - 1]);
return A[Right - 1];
}
快排代码:
public static int[] kuaiPai (int[] array,int begin, int end) {
if (array.length < 1 || begin < 0 || end >= array.length || begin > end) {
return null;
}
int i = begin;
int j = end;
int base = array[begin]; //选取基准值为第一个数
while (i<j) {
while (array[j]>base&&i<j) {
j--;
}
if (i<j) {
array[i] = array[j];
i++;
}
while (array[i]<=base&&i<j) {
i++;
}
if (i<j) {
array[j] = array[i];
j--;
}
}
array[i] = base;
kuaiPai(array,begin,i-1);
kuaiPai(array,i+1,end);
return array;
}