8中排序算法及其优化:采用Java语言描述

1、 冒泡排序及其优化

核心思想: 通过每一轮的比较,把较大的数字放在末尾,每一轮比较完之后,下一轮的比较的次数为该数组的长度减去循环次数再减一。
举例: 有以下数组 5 4 6 7 1 9 8
第一次循环:
第一趟比较:4 5 6 7 1 8 9
第二趟比较:4 5 6 1 7 8 9
第三趟比较:4 5 1 6 7 8 9
第四、五、六趟比较结果与第三趟一样
第二次循环:
第一趟比较:4 1 5 6 7 8 9
第二趟比较:1 4 5 6 7 8 9

举得例子比较特殊,两次循环就完成了排序。
代码如下:

public int[] BubbleSort(int arr)
{ 
       int temp = 0;
    	for(int i = 0; i < arr.length; i++)   //外层循环控制循环次数
    	{
    		for(int j = 0; j < arr.length-i-1; j++) //内层循环控制比较次数
    		{
    			if(arr[j]>arr[j+1] )
    			{
    				temp = arr[j];
    				arr[j] = arr[j+1];
    				arr[j+1] = temp;
    			}
    		}
    		return arr;
}

现在来思考这样一个问题,看一下数字:1,2,3,4,5,6,7,8,10,9;这段数字我们只需循环一次即可获得正确的排序,但是依据上面的代码,还是需要比较arr.length-1次,因此浪费时间,需要对其进行优化。我们可以在循环体内添加一个标记,来判断是否进行了比较,如果为进行比较则说明该数组本身就有序。

public int[] BubbleSor(int[] arr)
{
       for(int i = 0; i < arr.length;i++)
       { 
            int flag = 0; // 设置标记
            for(int j = 0; j < arr.length - i -1; j++)
            {
                 if(arr[j] > arr[j+1])
                 {
                       int temp = arr[j];
                       arr[j] = arr[j+1];
                       arr[j+1] == temp;
                       flag = 1;
                 }
            }
            if(flag == 0)
            {
                return arr;
            }
       } 
       return arr;
}

冒泡排序优化2:优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3 ,4 ,7,6,5)。但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化。既我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。

 public int[] BubbleSort(int[] arr)
    {
    	int temp = 0;
    	int flag = 0;
    	int k = arr.length-1;
    	int pos = 0;
    	for(int i = 0; i< arr.length;i++)
    	{
    		flag = 0;
    		for(int j = 0; j < k; j++)
    		{
    			if(arr[j] > arr[j+1])
    			{
    				temp = arr[j];
    				arr[j] = arr[j+1];
    				arr[j+1] = temp;
    				flag =1;
    				pos = j;
    			}
    		}
    		if(flag == 0)
    		{
    			return arr;
    		}
    		k = pos;
    	}
    	return arr;
    }

冒泡排序优化3:一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面。例如:排序数据1,2,3,4,5,6,0

 public int[] BubbleSort(int[] arr)
    {
    	int pos =0;//存储最后一个交换位置
    	int flag = 0; //存储是否交换过
    	int temp = 0;//临时变量
    	int n=0;
    	int k = arr.length -1;
    	for(int i = 0; i < arr.length; i++)
    	{
    		flag = 0;
    		for(int j = n; j < k; j++)
    		{
    			if(arr[j] > arr[j+1])
    			{
    				temp= arr[j];
    				arr[j] = arr[j+1];
    				arr[j+1] = temp;
    				pos = j;//获取最后交换位置
    				flag = 1;
    			}
    		}
    		if(flag == 0)
    		{
    			return arr;
    		}
    		k = pos;//下一次比较到记录位置即可
    		//反向查找
    		for(int j = k; j>n;j--)
    		{
    			if(arr[j] < arr[j-1])
    			{
    				temp = arr[j];
        			arr[j] = arr[j-1];
        			arr[j-1] = temp;
        			flag = 1;
    			}
    		}
    		n++;
    		if(flag == 0)
    		{
    			return arr;
    		}
    	}
    	return arr;
    }

2、直接插入排序

核心思想:把数组分成两个序列,一个有序序列和一个无序序列,将无序序列中的数字依次插入有序序列中,并保证有序序列中的数字任然有序。

public int[] sort(int[] arr)
	{
		int j = 0;
		for(int i = 1; i < arr.length; i++)
		{
			int temp = arr[i];
			j =i-1;
			while(j >=0&&temp<arr[j])
			{
				arr[j+1] = arr[j]; 
				j--;
			}														
		arr[j+1] = temp;
		}
		return arr;
	}

3、希尔排序

核心思想:希尔排序是将待排序的数组元素 按下标的一定增量分组 ,分成多个子序列,然后对各个子序列进行直接插入排序算法排序;然后依次缩减增量再进行排序,直到增量为1时,进行最后一次直接插入排序,排序结束。
有以下数组:5 6 4 1 12 10 3 7
**第一次:**增量为 gap = arr.length / 2,则将数组分成一下4组
第一次分组
第一次分组排完序之后结果为:5 6 3 1 12 10 4 7
第二次 : gap = gap / 2;
第二次分组
第二次分完组之后结果为:3,1,5,6,4,7,12,10
第三次 gap = gap / 2=1
第三次分组结果
对以上数组进行一次插入排序即可。
有了以上思路即可以写代码了。

public void swap(int[] arr,int first, int last)
	{
		int temp = arr[first];
		arr[first] = arr[last];
		arr[last] = temp;
	}
	public int[] shellSort(int[] arr)
	{
		for(int gap = arr.length / 2; gap > 0; gap/=2)
		{
			for(int i = gap;i < arr.length;i++)
			{
				int j = i;
				while(j-gap>=0&&arr[j]<arr[j-gap])
				{
					swap(arr, j,j-gap);
					j -= gap;
				}
			}
		}
		return arr;
	}

4、堆排序

什么是堆???
可以把堆看成是一颗完全二叉树,并且这颗完全二叉树有一个特点:根节点比其左右叶子结点的值都大或者都小。如果每一个根节点比其左右叶子节点的值都大,则称之为大顶堆,反之就称为小顶堆。这一规则必须在完全二叉树的任一子树都满足。
完全二叉树的特点:设根节点的索引值为index;
(1)左节点的索引值为 2indexe +1;
(2)右节点的索引值为 2
index+2;
(3)其父节点的值为(index-1)/2;
//构造小顶堆

public static void minHeap(int[] data,int size,int index)
{
    //左子节点
    int leftNode = 2*index+1;
    //右子节点
    int rightNode = 2*index+2;
    int max = index;
    //和两个子节点分别对比,找出最大的节点
    if(leftNode < size&&data[leftNode] <data[max])
    {
        max = leftNode;
    }
    if(rightNode< size&&data[rightNode] < data[max])
    {
        max = rightNode;
    }
    //交换位置
    if(max!=index)
    {
        int temp = data[index];
        data[index] = data[max];
        data[max] = temp;
        //递归进行该节点子树小顶堆的创建
        minHeap(data,size,max);
    }
}
//构造完全的小顶堆

public static void BuildminHeap(int[] data)
{
    int start = (data.length-1)/2;
    for(int i = start; i >=0;i--)
    {
        minHeap(data,data.length,i);
    }
}
//进行小顶堆排序
public static void minHeapSort(int[] data)
{
    for(int i = data.length-1; i>0;i--)
    {
        int temp = data[0];
        data[0] = data[i];
        data[i] = temp;
        minHeap(data, i, 0);
    }
}

5、快速排序

(1)挖坑法

 public int partition(int[] arr, int left, int right)
	{
		if(left<right)
		{
			int temp = arr[left];
			while(left<right)
			{
				//如果右边的数大于基数temp,则向左移动
				while(left<right&&arr[right] >= temp)
				{
					right--;
				}
				//如果不大于,则把右边的数赋值给左边
				arr[left] = arr[right];
				//如果左边的数小于基数,则向右移动
				while(left<right&&arr[left] <= temp)
				{
					left++;
				}
				//如果左边的数大于基数,则左边的数赋值给右边
				arr[right] = arr[left];
			}
			arr[left]=temp;
		}
		
		return left;
	}
 public int[] QSort(int[] arr, int left, int right)
 {
	 int[] newArr = arr;
	 if(left < right)
	 {
     int pivot = partition(newArr, left, right);
	 QSort(newArr,left,pivot-1);
	 QSort(newArr, pivot+1,right);
	 }
	 return newArr;
 }

###(2)快速排序的优化

三数取中法

public void MedianOfThree(int[] arr, int left, int right)
 {
	 int mid = left+(left+right);
	 if(arr[left] > arr[right])//把最大值放在数组的末尾
	 {
		 swap(arr, left, right);
	 }
	 if(arr[mid]> arr[right])//把最大值放在数组的末尾
	 {
		 swap(arr, mid, right);
	 }
	 if(arr[mid]>arr[left])//把最小值放在中间
	 {
		 swap(arr, mid, left);
	 }
 }
 //交换
 public void swap(int[] arr, int left, int right)
 {
		 int temp = arr[left];
		 arr[left] = arr[right];
		 arr[right] = temp;
 }
 public int[] QSort_MedianOfThree(int[] arr, int left, int right)
 {
	 //[1]先进行数字调换
	 MedianOfThree(arr,left,right);//将中间数放到开头
	 int[] newArr = arr;
	 if(left < right)
	 {
	 int pivot = partition(newArr, left,right);
	 QSort(newArr,left,pivot-1);
	 QSort(newArr, pivot+1,right);
	 }
	 return newArr;
 }

三数取中+插入排序

 public int[] insertSort(int[] arr)
 {
	 int j = 0;
	 for(int i = 1; i< arr.length; i++)
	 {
		 int temp = arr[i];
		 for( j = i-1; j >=0&&arr[j]>temp;j--)
		 {
			 arr[j+1] = arr[j];
		 }
		 arr[j+1] = temp;
	 }
	 return arr;
 }
 public int[] QSort_Insert(int[] arr, int left, int right)
 {
	 //如果数组的长度小于10,则采用插入排序
	 if(right - left +1 < 10)
	 {
		 return insertSort(arr);
	 }
	 //[1]先进行数字调换
	 MedianOfThree(arr,left,right);//将中间数放到开头
	 int[] newArr = arr;
	 if(left < right)
	 {
	 int pivot = partition(newArr, left,right);
	 QSort(newArr,left,pivot-1);
	 QSort(newArr, pivot+1,right);
	 }
	 return newArr;
 }

6、选择排序

核心思想:每次从数组中寻找最小的数字,放在数组的开头。思想比较简单,这里就不做过多的演示。

public int[] selectSort(int[] arr)
	{
		for(int i = 0;i < arr.length;i++)
		{
			int minIndex = i;
			//每次搜寻第i+1之后的元素
			for(int j = i+1;j < arr.length;j++)
			{
				if(arr[j] < arr[minIndex])
				{
					minIndex=j;
				}
			}
			if(i!=minIndex)
			{
				int temp = arr[i];
				arr[i] = arr[minIndex];
				arr[minIndex] = temp;
			}
		}
		return arr;
	}

7、基数排序

适合在待排序的数组中,个位、10位、百位…都有,数字差别较大。
基本思想: 将整数按位数切割成不同的数字,然后按每个位数分别比较。
**具体做法是:**将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
代码如下:
可以把代码中的二维数组想象成一个表格,便于理解。

public int[] radixSort(int[]A, int n)
	{
		int max = A[0];
		for(int i = 1; i< n; i++)
		{
			if(max < A[i])
			{
				max = A[i];
			}
		}
		double d = 	Math.pow(10, String.valueOf(max).length());
		
		int k = 1;
		int[][] t=new int[10][n];
		int[] num = new int[n];//记录每个桶中存入数的个数
		while(k < d)
		{
			for(int a :A)
			{
				int m = (a/k)%10;
				t[m][num[m]]=a;//把数字放入桶中
				num[m]++;
			}
			int c=0;
			for(int i =0; i<n;i++)
			{
				if(num[i] !=0)
				{
					for(int j=0; j < num[i];j++)
					{
						A[c++] = t[i][j];
					}
				}
				num[i] = 0;
			}
			k*=10;
		}
		return A;
	}

8、归并排序

public int[] sort(int[] arr, int low, int high)
	{
		int[] newArr = arr;
		int middle = (low+high)/2;
		if(low < high)
		{
			sort(newArr, low, middle);
			sort(newArr, middle+1,high);
			merge(newArr, low, middle, high);
		}
		return newArr;
	}
	public int[] merge(int[] arr, int low, int middle,int high)
	{
		//用域存储归并后的临时数组
		int[] temp = new int[high - low +1];
		//记录第一个数组中需要遍历的下标
		int i= low;
	    //记录第二个数组中需要遍历的下标
		int j = middle+1 ;
		//用于记录在临时数组中存放的下标
		int index = 0;
		//遍历两个数组,取出小的数字放入临时数组中
		while(i<=middle&&j<=high)
		{
			//把小的数据放入临时数组中
			if(arr[i] < arr[j])
			{
				temp[index] = arr[i];
				i++;
			}
			else
			{
				temp[index] = arr[j];
				j++;
			}
			index++;
		}
		//处理多余数据
		while(j<=high)
		{
			temp[index]=arr[j];
			j++;
			index++;
		}
		while(i<=middle)
		{
			temp[index]=arr[i];
			i++;
			index++;
		}
	    for(int k = 0; k < temp.length;k++)
	    {
	    	arr[k+low] = temp[k];
	    }
		return arr;
	}

猜你喜欢

转载自blog.csdn.net/rodman177/article/details/89603626
今日推荐