数据结构与算法之十大常见排序算法(上)
前言
本篇文章主要是对算法中常见的五种排序算法的总结和归纳,采用的语言为Java
下面正文:
一、排序算法是什么?
根据百度百科介绍:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。
而常见的排序有:(1)冒泡排序;(2)选择排序;(3)插入排序;(4)希尔排序;(5)快速排序;
(6)归并排序;(7)基数排序;(8)堆排序;(9)计数排序;(10)桶排序。
本文主要介绍前5种算法,5种算法函数都写了一个Sort类中;
二、算法:
1.冒泡排序
冒泡排序是从第1个元素开始,由第1个元素与第2个元素相比,然后第2个与第3个元素相比,依次类推,比较过程中,以小的在前大的在后为真,不是的话交换两者位置,因其元素在数组中的移动像泡从水里冒出一样,大的值依次往上走,因而叫冒泡排序。
代码如下(示例):
static void bub_sort(int[] arr,int len)
{
int j;
int temp; //将for循环中重复定义的变量单独定义,避免重复定义导致的效率降低
for(int i = 0;i<len-1;i++)
{
for(j=0;j<len-i-1;j++)
{
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
2.选择排序:
选择排序是指从一个数组中,由第一个元素开始,寻找最小的一个值,然后将最小的值放在第一个位置,然后再从第二个元素开始,再找最小的值,放在第二个位置,以此类推,直到最后一个值确定。
代码如下(示例):
//选择排序
static void select_sort(int[] arr,int len)
{
int temp;
int min;
int j;
for (int i = 0; i < len - 1; i++)
{
min = i;
for (j = i + 1; j < len; j++)
{
if (arr[j] < arr[min])
min = j;
}
temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
3.插入排序:
插入排序是将数组从第一个元素开始,将第一个元素看作一个有序数组,然后由第二个元素与这个有序数组中的值由后往前依次比较,然后插入到这个有序数组中合适的位置,形成一个新的、有两个元素的有序数组,然后又由第三个元素与这个有序数组比较,然后插入到其中的合适位置,再次形成新的有序数组,以此类推…
图片来源与网络:
代码如下(示例):
//插入排序
static void insert_sort(int[] arr,int len)
{
int j,temp;
for(int i = 0;i < len - 1;i++)
{
for(j = i + 1;j > 0;j--)
{
if(arr[j]<arr[j - 1])//以此进行相邻比较,然后j--使其能不断向前比较,直到j < 0循环结束。
{
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
}
4.希尔排序:
希尔排序简单的讲可以看作是插入排序的升级版,它实际上是根据增量来将数组划分成许多个小块,每个小块内部进行插入排序,然后再根据增量的改变,划分新的小块,小块内部再次进行插入排序,直到最后增量为1,就完全等同与一个插入排序。因为这样的分块排序,使得希尔排序比插入排序的效率更高。
注意:增量的取值一般开始时为数组长度的一半,但这并不是最优增量,因为这个增量效果已经很好且方便,因此选择长度一般作为增量,每循环完一个增量减半直到1位置。
图片来源于网络:
代码如下(示例):
//希尔排序
static void shell_sort(int[] arr, int len) {
int j;
int gap = len >> 1; //此处设置增量gap = len的一半
int temp;
while (gap > 0) {
//分块排序开始,注:分块的内部插入排序不是像传统插入一样挨着来的,而是每个分块交替同时进行。
for (int i = gap; i < 7; i++) {
temp = arr[i];//将第i处的值取出
j = i - gap;
while (j >= 0 && temp < arr[j]) {//此处是以增量gap的值分组进行内部插入排序
arr[j + gap] = arr[j]; //交换分块内部值,形成新的有序小块
j -= gap;
}
arr[j + gap] = temp;//将temp放入它该去的地方
}
gap >>= 1;//增量取自身一半,逐渐缩小范围到1为止。
}
for(int i = 0;i < len;i++)
{
System.out.println(arr[i]);
}
}
5.快速排序
快速排序顾名思义,是这几种中最优效率最高的方法,但在数组长度较小时,其效率与希尔排序相差无几甚至不如希尔排序,因为快速排序使用了递归,而递归导致的频繁压栈出栈使得其在较小排序时效率反不如某些简单排序。但其在排序超大数据时的效率远超其他算法。据说许多大小公司在面试时经常会问到快速排序相关算法,因此掌握快速排序和它的递归思想是必须的。
其原理是选定一个数为基准数设为flag,一般是数组中第一个数,设一个下标为f,f从下标 f=基准数下标+1 开始,再设一个下标为b,b从下标 b=数组长度 开始,若下标f对应的数 arr[f]比基准数小,则 f++向右移动,然后再与基准数比较,直到arr[f] > flag为止,锁定f,然后b开始进行比较,arr[b]比基准数大,则b–向左继续移动,直到arr[b] < flag,锁定b,此时f和b对应的数组值 进行交换,然后f又开始移动,再次直到arr[f]>flag,锁定f,b开始移动,直到arr[b] <flag,交换f,b对应的数组值,由此循环,直到f和b下标重叠。此时b的左边一个数就应当是基准数所应该在的位置,将基准数flag与arr[b-1]交换值,然后再以这个b-1的位置,将数组切分成两块,分别传入自身函数进行递归。
可能难以理解,动手按照这个逻辑画画图就会比较清晰了。
图片来源与网络:
t
代码如下(示例):
//快速排序
static void fast_sort(int[] arr,int low,int height)
{
if(low >= height) return;
int flag = arr[low]; //基准数
int f = low+1;
int b = height;
int temp;
while(f<=b)
{
while(f<=b && arr[f] <= flag) f++;//直到arr[f] > flag;
while(f<=b && arr[b] >= flag) b--;//直到arr[b] < flag;
if(f<b)
{//交换arr[f]和arr[b]
temp = arr[f];
arr[f] = arr[b];
arr[b] = temp;
f++;
b--;
}
}
//将基准数与arr[b]交换,因为arr[b]的值正应当是基准数在有序数列中所处的位置
arr[low] = arr[b];
arr[b] = flag;
fast_sort(arr,low,b-1);//新的左半块数组
fast_sort(arr,b+1,height);//新的右半块数组
}
}
三、完整代码
public class Sort {
//冒泡排序
static void bub_sort(int[] arr,int len)
{
int j;
int temp; //将for循环中重复定义的变量单独定义,避免重复定义导致的效率降低
for(int i = 0;i<len-1;i++)
{
for(j=0;j<len-i-1;j++)
{
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
//选择排序
static void select_sort(int[] arr,int len)
{
int temp;
int min;
int j;
for (int i = 0; i < len - 1; i++)
{
min = i;
for (j = i + 1; j < len; j++)
{
if (arr[j] < arr[min])
min = j;
}
temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
//插入排序
static void insert_sort(int[] arr,int len)
{
int j,temp;
for(int i = 0;i < len - 1;i++)
{
for(j = i + 1;j > 0;j--)
{
if(arr[j]<arr[j - 1])
{
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
}
//希尔排序
static void shell_sort(int[] arr, int len) {
int j;
int gap = len >> 1;
int temp;
while (gap > 0) {
for (int i = gap; i < 7; i++) {
temp = arr[i];
j = i - gap;
while (j >= 0 && temp < arr[j]) {
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = temp;
}
gap >>= 1;
}
for(int i = 0;i < len;i++)
{
System.out.println(arr[i]);
}
}
//快速排序
static void fast_sort(int[] arr,int low,int height)
{
if(low >= height) return;
int flag = arr[low];
int f = low+1;
int b = height;
int temp;
while(f<=b)
{
while(f<=b && arr[f] <= flag) f++;
while(f<=b && arr[b] >= flag) b--;
if(f<b)
{
temp = arr[f];
arr[f] = arr[b];
arr[b] = temp;
f++;
b--;
}
}
arr[low] = arr[b];
arr[b] = flag;
fast_sort(arr,low,b-1);
fast_sort(arr,b+1,height);
}
//输出数组值
static void print(int[] arr)
{
for(int x:arr) System.out.print(x);
System.out.println("\n=====================================");
}
}
Public class test{
public static void main(String[] args){
int[] arr00 = {4, 3, 9, 9, 3, 3, 6, 6, 1, 2, 5, 7, 7, 7, 8, 7, 3, 1, 2, 5, 5, 1, 3, 4, 4, 3, 6, 8, 1, 2, 4, 5};
Sort.bub_sort(arr00,32);
Sort,print(arr00);
int[] arr11 = {4, 3, 9, 9, 3, 3, 6, 6, 1, 2, 5, 7, 7, 7, 8, 7, 3, 1, 2, 5, 5, 1, 3, 4, 4, 3, 6, 8, 1, 2, 4, 5};
Sort.insert_sort(arr11, 32);
Sort,print(arr11);
int[] arr22 = {4, 3, 9, 9, 3, 3, 6, 6, 1, 2, 5, 7, 7, 7, 8, 7, 3, 1, 2, 5, 5, 1, 3, 4, 4, 3, 6, 8, 1, 2, 4, 5};
Sort.select_sort(arr22, 32);
Sort,print(arr22);
int[] arr33 = {4, 3, 9, 9, 3, 3, 6, 6, 1, 2, 5, 7, 7, 7, 8, 7, 3, 1, 2, 5, 5, 1, 3, 4, 4, 3, 6, 8, 1, 2, 4, 5};
Sort.bub_sort(arr33,32);
Sort,print(arr33);
int[] arr44 = {4, 3, 9, 9, 3, 3, 6, 6, 1, 2, 5, 7, 7, 7, 8, 7, 3, 1, 2, 5, 5, 1, 3, 4, 4, 3, 6, 8, 1, 2, 4, 5};
Sort.fast_sort(arr44,0,31);
Sort,print(arr44);