public void quickSort(int[] arr,int start,int end){
//递归终止条件
if(start >= end){
return;
}
//一、挖坑法实现快排
int left = start;
int right = end;
int mid = arr[left];//取头尾位置法获得基准值(可改成随机选取基准位置或三数取中法进行初步优化)
while(left < right){
//一定要加上left<right,不然可能出现左右指针交叉,即错过相等的机会,导致基准值插入错误
while(left < right && arr[right] >= mid){//一定要等于号,避免出现指针指向都等于mid时死循环
right--;
}
arr[left] = arr[right];
//一定要加上left<right,不然可能出现左右指针交叉,即错过左右指针相等的机会,导致基准值插入错误
while (left < right && arr[left] <= mid){//一定要等于号,避免出现指针指向都等于mid时死循环
left++;
}
arr[right] = arr[left];
}
//左右指针此时指向相同,指向即为基准数的位置
arr[left] = mid;
//左递归
quickSort(arr,start,left - 1);
//右递归
quickSort(arr,left + 1,end);
}
2. 冒泡排序
//冒泡排序
public void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {//i < arr.length - 1,确认了arr.length - 1个数的位置,也即确认完了arr.length个数的位置
for (int j = 1; j < arr.length - i; j++) {
if (arr[j - 1] > arr[j]) {
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
}
}
}
}
//冒泡排序优化一
public void bubbleSort1(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
boolean flag = true;//标记数组是否已经有序,有序则提前结束,避免无效比较
for (int j = 1; j < arr.length - i; j++) {
if (arr[j - 1] > arr[j]) {
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
flag = false;//发生交换,即数组无序,将标记置为false
}
}
if(flag){//标记未被置为false,即整轮比较过程没有发生交换,数组已然有序
break;//提前退出循环
}
}
}
//冒泡排序优化二
public void bubbleSort2(int[] arr) {
boolean flag;
int tmp;
int lastExchangeIndex = 0; //用该变量记录最后一次交换的位置
int sortBorder = arr.length - 1; //无序数列的边界,每次比较只需要比到这里为止
for (int i = 0; i < n; ++i)
{
flag = true;
for (int j = 0; j < sortBorder; j++)
{
if (a[j] > a[j + 1])
{
tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
flag = false;
lastExchangeIndex = j; //把无序数列的边界更新为最后一次交换元素的位置
}
}
sortBorder = lastExchangeIndex;
if (flag)
break;
}
3. 选择排序
public void selectionSort(int[] arr) {
int max = 0;
int maxID = 0;
for (int i = 0; i < arr.length - 1 ; i++) {//i < arr.length - 1,确认了arr.length - 1个数的位置,也即确认完了arr.length个数的位置
for (int j = 0; j < arr.length - i; j++) {//每小轮确定出一个最大值与其下标
if(arr[j] > max){
max = arr[j];
maxID = j;
}
}
int tem = arr[arr.length - i - 1];//将末尾值与最大值交换,确定一个最值位置
arr[arr.length - i - 1] = max;
arr[maxID] = tem;
max = 0; //置零
}
4. 直接插入排序
public void insertionSort(int[] arr, int start, int end) {
int tmp;
int insertIndex;
for (int i = 1; i < arr.length; i++) {//从数组下标1开始往前即有序部分进行插入
tmp = arr[i];//待插入值
insertIndex = i - 1;//待插入位置下标,预设为待插入值的前一个位置(也即数组有序部分的最后一个位置)
while ( insertIndex >= 0 && tmp < arr[ insertIndex]) {//当前待插入值小于数组前部的有序部分
arr[ insertIndex + 1] = arr[ insertIndex];//将数组前面的有序部分进行后移
insertIndex--;//不断向前进行比较,直到找到合适的插入位置
}//while循环结束即找到合适的插入位置
if ( insertIndex != i - 1) {//当 insertIndex发生变化,即可以进行一次插入
arr[ insertIndex + 1] = tmp;
}
}
5. 希尔排序
//交换实现
public void shellSort(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {//增量gap采用gap = gap / 2的方法来进行缩小增量
for (int i = gap; i < arr.length; i++) {//根据分组情况得出,需要比较arr.length - gap小轮
for (int j = i - gap; j >= 0; j -= gap) {//每次交换完后还需跟前面同组的元素再比较大小
if (arr[j] > arr[j + gap]) {//进行比较符合交换条件时交换(跨步长比较,即同组比较)
int tem = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = tem;
}
}
}
}
}
//移位实现,该方法更优
public void shellSort1(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {//增量gap采用gap = gap / 2的方法来进行缩小增量
for (int i = gap; i < arr.length; i++) {//这里相当于直接插入排序
int tmp = arr[i];//待插入元素的值
int k = i-gap;//待插入元素的前一个元素下标,从这里开始比较,寻找插入位置
while( k >= gap && tmp < arr[k] ){//当向前比较不越界, 且插入值比当前比较值小时
arr[k + gap] = arr[k];//大的往后移,直到找到正确合适的位置
k -= gap;//依次向前比较
}//while循环退出后找到正确的插入位置,即不满足前一个比待插入值大,此时位置正确
if(k != i - gap){//k值发生变化
arr[k + gap] = tmp;//将值插入
}
}
}
}
6. 归并排序
public void mergeSort(int[] arr, int left, int right) {
if(left == right)
return;
int mid = left + ((right - left) >> 1);
mergeSort(arr,left,mid);
mergeSort(arr,mid + 1,right);
merge(arr,left,mid,right);
}
private void merge(int[] arr, int left, int mid, int right) {
int[] tmp = new int[right - left + 1];
int i = 0;//用于更新tmp数组
int p1 = left;
int p2 = mid + 1;
// 比较左右两部分的元素,哪个小,把那个元素填入tmp数组中
while(p1 <= mid && p2 <= right){
//arr[p1] <= arr[p2],取arr[p1],这样可以保证归并排序为稳定排序
tmp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
// 上面的循环退出后,把剩余的元素依次填入到temp中
// 以下两个while只有一个会执行
while(p1 <= mid){
tmp[i++] = arr[p1++];
}
while(p2 <= right){
tmp[i++] = arr[p2++];
}
//将tmp数组赋值给原数组
for (int j = 0; j < tmp.length; j++) {
arr[left + j] = tmp[j];
}
}
7. 计数排序
public void countingSort(int[] arr) {
//计数排序
//找出最大值最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
//创建计数数组,大小为max - min + 1
int[] count = new int[max - min + 1];
//遍历数组进行计数
for (int i = 0; i < arr.length; i++) {
count[arr[i] - min]++;
}
//提取计数结果完成排序
int index = 0;
for (int i = 0; i < count.length; i++) {
while (count[i] > 0) {
arr[index++] = i + min;
count[i]--;
}
}
}
8. 桶排序
public static bucketSort(double[] arr){
//得到数列的最大值和最小值,并计算出差值d
double max=arr[0];
double min=arr[0];
for (int i=1;i<arr.length;i++){
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
double d=max-min;
//初始化桶
int bucketNum=arr.length;
ArrayList<LinkedList<Double>> bucketList=new ArrayList<LinkedList<Double>>(bucketNum);
for (int i=0;i<bucketNum;i++){
bucketList.add(new LinkedList<Double>());
}
//遍历原始数组根据映射规则将每个元素放入桶中
for (int i=0;i<arr.length;i++){
int num=(int)((arr[i]-min)*(bucketNum-1)/d);
bucketList.get(num).add(arr[i]);
}
//对每个桶内部进行排序
for(int i=0;i<bucketList.size();i++){
// 使用Collections.sort,其底层实现基于归并排序或归并排序的优化版本
Collections.sort(bucketList.get(i));
}
// 将桶中的元素赋值到原数组
int index=0;
for (LinkedList<Double> list:bucketList) {
for (double element:list){
arr[index++]=element;
}
}
}
9. 基数排序
public static void radixSort(int[] arr) {
// 存数组中最大的数字,为了知道循环几次
int max = Integer.MIN_VALUE;// (整数中的最小数)
// 遍历数组,找出最大值
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
// 计算最大数是几位数,此方法计算绝妙
int maxLength = (max + "").length();
// 用于临时存储数据的数组
int[][] tmp = new int[10][arr.length];
// 用于存储桶内的元素位置
int[] counts = new int[arr.length];
// 第一轮个位数较易得到余数,第二轮就得先除以十再去取余,之后百位除以一百
// 可以看出,还有一个变量(即以下变量n)随循环次数变化,为了取余
// 循环的次数
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
// 每一轮取余
for (int j = 0; j < arr.length; j++) {
// 计算余数
int remainder = (arr[j] / n) % 10;
// 把遍历得到的数据放在指定数组中,有两个信息,放在第几个桶+数据应该放在第几位
tmp[remainder][counts[remainder]] = arr[j];
// 记录数量
counts[remainder]++;
}
// 记录取的数字应该放到位置
int index = 0;
// 每一轮循环之后把数字取出来
for (int k = 0; k < counts.length; k++) {
// 记录数量的数组中当前余数记录不为零
if (counts[k] != 0) {
for (int l = 0; l < counts[k]; l++) {
// 取出元素
arr[index++] = tmp[k][l];
}
// 取出后把数量置为零
counts[k] = 0;
}
}
}
}
1. 快速排序public static void quickSort(int[] arr,int start,int end){ //一、挖坑法实现快排 int left = start; int right = end; int mid = arr[left]; while(left < right){ //一定要加上left<right,不然可能出现左右指针交叉,即错过相等的机会,导致