1. 希尔排序(对插入排序的一个优化)
解决方法
先间隔排序,每4个为一个间隔,然后再进行插入排序
那么到底隔多少个元素当做一个间隔呢?有一个公式
h = (3 k - 1 ) / 2
并且 h < n / 3 (h小于数组的三分之一)
代码实现
public static void countShellNum(int n){
int k = 1;
int h;
ArrayList<Integer> arrayList = new ArrayList<Integer>();
do{
h = ((int)Math.pow(3,k) - 1) / 2;
if(h > n /3){
break;
}
arrayList.add(h); // 1,4,13,40
k++;
}while(h <= n / 3);
}
先对间隔为4的数组进行排序,接着在对1排序,根据arrayList存储的元素倒序排序
排序过程步骤
。。。
希尔排序的代码
public static void shellSort(int[] arr){
int length = arr.length;
int k = 1;
int h;
ArrayList<Integer> arrayList = new ArrayList<Integer>();
do{
h = ((int)Math.pow(3,k) - 1) / 2;
if(h > length /3){
break;
}
arrayList.add(h);
k++;
}while(h <= length / 3);
for(int a = arrayList.size() -1 ; a >=0 ;a--){
h = arrayList.get(a);
// 先写一个未优化的插入排序
for(int i = h; i < length ; i++){
for(int j = i ; j >= h ; j = j-h){
if(arr[j] < arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}else{
break;
}
}
}
}
}
希尔排序时间复杂度
O(n3/2)
空间复杂度
O(1)
2. 归并排序
总体思想:
先把大数组拆分成小数组,一直拆到剩一个元素为止,接着在从小数组排序合并成为一个大数组
代码实现
public class GuiBingSort {
/**
* 归并的步骤
* 1.将大问题拆分为小问题
* 2.写出终止条件
*/
public void sort(int[] arr,int left,int right){
// 2.终止条件
if(left == right){
return;
}
int mid = (left+right) / 2;
// 1.拆小问题
sort(arr,left,mid);
sort(arr,mid+1,right);
merge(arr,left,mid,right);
}
private void merge(int[] arr, int left, int mid, int right) {
//1 创建一个新数组
int num = right-left +1;
int[] temp = new int[num];
//2.定义指针
int tempPos = 0;
int i = left;
int j = mid +1;
while(i <= mid && j <= right){
if(arr[i] < arr[j]){
temp[tempPos++] = arr[i++];
}else{
temp[tempPos++] = arr[j++];
}
}
while(i <= mid){
temp[tempPos++] = arr[i++];
}
while(j <= right){
temp[tempPos++] = arr[j++];
}
// 3.最后还需要将新数组的值复制到老数组中
for(int k = 0 ; k < temp.length ; k++){
arr[left++] = temp[k];
}
}
}
归并排序空间复杂度为O(n)
另一种实现方式
3. 快速排序(快排)
先随机找一个分区点,让后将数组中小于分区点的元素放到分区点左边,大于分区点的元素放到分区点右边
快排细节
public class QuickSort {
public void sort(int arr[]){
if(arr == null || arr.length < 0){
return ;
}
sort(arr,0,arr.length-1);
}
private void sort(int[] data,int lo,int hi){
//递归终止条件
if(lo >= hi) return;
int j = partition(data,lo,hi);
sort(data,lo,j-1);
sort(data,j+1,hi);
}
private int partition(int[] data, int lo, int hi) {
//定义分区点
int pivot = data[hi];
int great = lo;
int less = lo;
for(;great <= hi -1; great++){
if(data[great] < pivot){
int temp = data[great];
data[great] = data[less];
data[less] = temp;
less++;
}
}
//less和pivot调换位置
int temp = data[less];
data[less] = data[hi];
data[hi] = temp;
return less;
}
}
快速排序
时间复杂度:O(nlogn)
空间复杂度:O(logn)
在代码中没有新建数组呀,为什么不是O(1)。是因为我们用的是递归的写法,递归每调用一次都会占用一次栈的内存。
快速排序不是稳定的排序算法
三路快排代码实现
public class ThreeWayQuickSorter extends Sorter {
public void sort(int[] data) {
if (data == null || data.length <= 1) return;
sort(data, 0, data.length - 1);
}
private void sort(int[] data, int lo, int hi) {
if (lo >= hi) return;
// 分区
int pivot = data[hi];
int less = lo;
int great = hi;
int i = lo;
while (i <= great) {
if (data[i] < pivot) {
swap(data, i, less);
less++;
i++;
} else if (data[i] > pivot) {
swap(data, i, great);
great--;
} else {
i++;
}
}
sort(data, lo, less - 1);
sort(data, great +1, hi);
}
public static void main(String[] args) {
int[] data = new int[]{34, 33, 12, 78, 21, 1, 98, 100};
new ThreeWayQuickSorter().sort(data);
System.out.println(Arrays.toString(data));
}
}
4. 桶排序
5. 计数排序
public class CountingSorter {
public void sort(int[] data) {
if (data == null || data.length <= 1) return;
// 1. 找到数组中的最大值,初始化计数器
int max = data[0];
int min = data[0];
for (int i = 1; i < data.length; i++) { // O(n)
max = Math.max(max, data[i]);
min = Math.min(min, data[i]);
}
int[] count = new int[max - min + 1];
// 2. 计数
for (int i = 0; i < data.length; i++) { // O(n)
count[data[i] - min]++;
}
// 3. 计数累加
for (int i = 1; i < count.length; i++) { // O(k)
count[i] += count[i - 1];
}
// 4. 计算每个元素在排序数组中的位置
int[] output = new int[data.length];
for (int i = data.length - 1; i >= 0; i--) { // O(n)
int j = data[i];
int k = count[j - min] - 1;
output[k] = data[i];
count[j - min]--;
}
// 5. 拷贝数组
for (int i = 0; i < data.length; i++) { // O(n)
data[i] = output[i];
}
}
public static void main(String[] args) {
CountingSorter sorter = new CountingSorter();
int[] data = { 4, 2, -2, 8, 3, 3, 1 };
sorter.sort(data);
System.out.println(Arrays.toString(data));
}
}
6. 基数排序
基数排序,其实本质上是计数排序
public class RadixSorter {
public void sort(int[] data) {
if (data == null || data.length <= 1) return;
// 1. 找到最大值 4006869915
int max = data[0];
for (int i = 1; i < data.length; i++) {
max = Math.max(max, data[i]);
}
// 2. 对数组按照每个元素的每位进行计数排序
for (int exp = 1; max / exp > 0; exp *= 10) { // O(n)
countSort(data, exp); // 时间复杂度:O(n)
}
}
private void countSort(int[] data, int exp) { // 时间复杂度:O(n)
// 之所以是 10,是因为数字只有 0...9 十个数字
int[] count = new int[10];
for (int i = 0; i < data.length; i++) {
// 个位数: (234 / 1) % 10 = 4
// 十位数: (234 / 10) % 10 = 3
// 百位数: (234 / 100) % 10 = 2
int digit = (data[i] / exp) % 10;
count[digit]++;
}
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
int[] output = new int[data.length];
for (int i = data.length - 1; i >= 0; i--) {
int digit = (data[i] / exp) % 10;
int k = count[digit] - 1;
output[k] = data[i];
count[digit]--;
}
for (int i = 0; i < data.length; i++)
data[i] = output[i];
}
public static void main(String[] args) {
int[] data = new int[]{4512, 4231, 31923, 2165, 1141};
new RadixSorter().sort(data);
System.out.println(Arrays.toString(data));
}
}
7. java内置排序算法
引用型数据类型怎么比较
实现comparable类,重写方法
如果想比较的对象没有实现comparable 就用第二种方式:
第二种写法
动态数组的排序