排序算法是数据结构中十分基础的内容,本文总结了常用的排序算法的原理和性能,还给出了相关的图解,并且采用java语言实现了算法,最后给了一个面试中实际的例子,以及算法复杂度的比较
// 选择排序
public int[] selectsort(int[] arr)
{
for(int x=0;x1;x++) //最后一个数不用在自己和自己进行比较了,n-1轮
{
for(int y=x+1;yif(arr[x]>arr[y])
{
int temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
}
return arr;
}
将当前元素和左边的元素比较,若当前元素小,就交换两者,也就相当于插入
// 插入排序
public int[] insertionSort(int[] arr) {
int len = arr.length;
for (int i = 1; i < len; i++)
{
// j表示当前元素的位置,将其和左边的元素比较,若当前元素小,就交换,也就相当于插入
// 这样当前元素位于j-1处,j--来更新当前元素,j一直左移不能越界,因此应该大于0
for(int j=i; j>0 && arr[j]1];j--)
{
int temp = arr[j]; // 元素交换
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
return arr;
}
相邻的两个元素进行比较,如果符合条件就换位,这样第一轮,最大的数会在最后面,长度在依次递减
// 冒泡排序
public int[] bubbleSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { // 相邻元素两两对比
int temp = arr[j+1]; // 元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
/**
* 将数组的某一段元素进行划分,小的在左边,大的在右边
*/
public static int divide(int[] a, int start, int end){
//每次都以最右边的元素作为基准值
int base = a[end];
//start一旦等于end,就说明左右两个指针合并到了同一位置,可以结束此轮循环。
while(start < end){
while(start < end && a[start] <= base)
//从左边开始遍历,如果比基准值小,就继续向右走
start++;
//上面的while循环结束时,就说明当前的a[start]的值比基准值大,应与基准值进行交换
if(start < end){
//交换
int temp = a[start];
a[start] = a[end];
a[end] = temp;
//交换后,此时的那个被调换的值也同时调到了正确的位置(基准值右边),因此右边也要同时向前移动一位
end--;
}
while(start < end && a[end] >= base)
//从右边开始遍历,如果比基准值大,就继续向左走
end--;
//上面的while循环结束时,就说明当前的a[end]的值比基准值小,应与基准值进行交换
if(start < end){
//交换
int temp = a[start];
a[start] = a[end];
a[end] = temp;
//交换后,此时的那个被调换的值也同时调到了正确的位置(基准值左边),因此左边也要同时向后移动一位
start++;
}
}
//这里返回start或者end皆可,此时的start和end都为基准值所在的位置
return end;
}
/**
* 排序
*/
public static void sort(int[] a, int start, int end){
if(start > end){
//如果只有一个元素,就不用再排下去了
return;
}
else{
//如果不止一个元素,继续划分两边递归排序下去
int partition = divide(a, start, end);
sort(a, start, partition-1);
sort(a, partition+1, end);
}
}
package cn;
import java.util.Arrays;
// 归并排序
public class MergeSort {
public static void main(String []args)
{
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr,0,8);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr,int start,int end)
{
if(startint mid = (start+end)/2;
sort(arr,start,mid);//左边归并排序,使得左子序列有序
sort(arr,mid+1,end);//右边归并排序,使得右子序列有序
merge(arr,start,mid,mid+1,end);//将两个有序子数组合并操作
}
}
private static void merge(int[] arr,int start1,int end1,int start2,int end2)
{
// 建立辅助数组
int len=end2-start1+1;
int[] temp=new int[len];
int i = start1;//左序列指针
int j = start2;//右序列指针
int t = 0;//临时数组指针
while (i<=end1 && j<=end2)
{ // 将小的放入辅助数组
if(arr[i]<=arr[j])
{
temp[t++] = arr[i++];
}else
{
temp[t++] = arr[j++];
}
}
//若左序列此时还有有剩余的,将左边剩余元素填充进temp中
while(i<=end1)
{
temp[t++] = arr[i++];
}
//若右序列此时还有有剩余的,将右序列剩余元素填充进temp中
while(j<=end2)
{
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(start1 <= end2){
arr[start1++] = temp[t++];
}
}
}
结果
[1, 2, 3, 4, 5, 6, 7, 8, 9]
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:父节点比较大的是大顶堆,父节点比较小的是小顶堆
堆排序基本思想及步骤
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将最大值放到末尾(最大值和末尾元素交换),此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了.
再简单总结下堆排序的基本思路:
a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素”沉”到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
package cn;
import java.util.Arrays;
/*
* 堆排序demo
*/
public class HeapSort
{
public static void main(String []args)
{
int []arr = {1,7,2,9,3,8,6,5,4};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr)
{
//1.构建大顶堆
for(int i=arr.length/2-1;i>=0;i--)
{
//从最后一个一个非叶子结点i从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
}
}
/*
* 调整大顶堆
*/
public static void adjustHeap(int []arr,int i,int length)
{
//i是父节点
while(2*i+1// k是子节点
int k=i*2+1;
//从i结点的左子结点开始,也就是2i+1处开始,使得k指向i的左右子节点中较大的一个
// 如果不存在右子节点,就不用比较了
if(k+11])
{
//如果左子结点小于右子结点,k指向右子结点
k++;
}
// 比较该较大节点与父节点
if(arr[k] break;
//如果子节点大于父节点,将子节点和父节点交换
swap(arr,i,k);
// 更新父节点,调整下面的子树
i = k;
}
}
/*
* 交换元素
*/
public static void swap(int []arr,int a ,int b)
{
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
希尔排序为了加快速度简单地改进了插入排序。 使数组中任意间隔为gap的元素都是有序的。下图中gap=4:
package cn;
import java.util.Arrays;
/*
*希尔排序
*/
public class ShellSort {
public static void main(String []args){
int []arr ={1,4,2,7,9,8,3,6};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int [] arr)
{
//确定初始的增量gap,保证其不能越界
int gap=1;
while(gap3)
gap=3*gap+1;
// 对于相隔gap的元素进行插入排序
while(gap>=1)
{
for(int i=gap;ifor(int j=i;j>=gap && arr[j]3;
}
}
/*
* 交换数组元素
*/
public static void swap(int []arr,int a,int b){
arr[a] = arr[a]+arr[b];
arr[b] = arr[a]-arr[b];
arr[a] = arr[a]-arr[b];
}
}
结果:
[1, 2, 3, 4, 6, 7, 8, 9]
对于公司中所有员工的年龄进行排序,员工大概有几万人,要求时间效率是o(n),可以使用辅助空间但是不能超过O(n)
package cn;
import java.util.Arrays;
public class SortAges {
public static void SortAges(int ages[],int length)
{
if(ages==null || length<=0)
return;
// 存放各个年龄出现的次数,年龄范围是0-99,初始值全都是0
int[] timesOfAge=new int[100];
for(int i=0;i<=99;i++)
{
timesOfAge[i]=0;
}
// 遍历数组,统计次数
for(int i=0;iint age=ages[i];
if(age<0||age>99)
throw new RuntimeException();
timesOfAge[age]++;
}
// 年龄排序一定是0 1 2、、99这种,关键在于需要设置几次,遍历所有年龄,该年龄出现几次,就在ages数组中写几次该年龄
int index=0;
for(int i=0;i<=99;i++)
{
for(int j=0;jpublic static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr={1,3,3,44,5,6,55,55,55};
SortAges(arr,9);
System.out.println(Arrays.toString(arr));
}
}
结果:
[1, 3, 3, 5, 6, 44, 55, 55, 55]