1、冒泡排序
比较相邻元素,将小的元素交换到前面,时间复杂度为O(n2),空间复杂度O(1),稳定排序。
void bubbleSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
for(int i = 0; i < nums.length -1; i++)
{
for(int j = 0; j < nums.length - i - 1; j++)
{
if(nums[j] < nums[j + 1])
{
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
2、选择排序
从待排序序列中选出最小元素,与待排序序列初始位置的元素进行交换,重复操作直到待排序元素个数为0,时间复杂度为O(n2),空间复杂度O(1),不稳定排序。
void selectionSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
for(int i = 0; i < nums.length; i++)
{
int min = nums[i];
int k = i;
for(int j = i + 1; j < nums.length; j++)
{
if(nums[j] < min)
{
min = nums[j];
k = j;
}
}
nums[k] = nums[i];
nums[i] = min;
}
}
3、插入排序
将待排序元素逐个插入到已排序序列中。
最坏时间复杂度为O(n2),最好时间复杂度为O(n),平均时间复杂度为O(n2),空间复杂度O(1),稳定排序。
void insertSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
for(int i = 1; i < nums.length; i++)
{
int j = i - 1;
int temp = nums[i];
while(j >= 0 && nums[j] > temp)
{
nums[j + 1] = nums[j];
j--;
}
nums[j + 1] = temp;
}
}
4、快速排序
实际应用中快速排序是表现最好的算法。
快速排序选取基准数,定义左右指针,先移动右指针,当右指针指向的元素比基准数小时停下,再移动左指针,当左指针指向的元素比基准数大时停下,交换左右指针指向的数,重复上述操作,直到左右指针相遇,交换基准数与指针相遇位置的数,再对基准数的左右部分执行相同的排序操作。
快速排序是不稳定的,最好情况时间复杂度为O(nlogn),最坏情况时间复杂度为O(n2),平均时间复杂度为O(nlogn),空间复杂度O(logn)
void quickSort(int [] nums, int left, int right)
{
if(left >= right)
return;
int i = left;
int j = right;
int key = nums[left];
while(i < j)
{
while(i< j && nums[j] > key)
j--;
while(i < j && nums[i] <= key)
i++;
if(i < j)
{
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
nums[left] = nums[i];
nums[i] = key;
quickSort(nums, left, i - 1);
quickSort(nums, i + 1, right);
}
5、堆排序
利用堆实现的选择排序,将堆看成完全二叉树。
最大堆要求节点的元素都要不小于其孩子,最小堆要求节点元素都不大于其左右孩子。
堆排序先调用buildMaxHeap将数组改造为最大堆,然后将堆顶和堆底元素交换,之后将底部上升,然后重新调用maxHeapify(堆有序化)保持最大堆性质。
时间复杂度为O(nlogn),空间复杂度O(1),不稳定排序。
void heapSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
int len = nums.length;
buildMaxHeap(nums, len);
for(int i = len - 1; i > 0; i--)
{
swap(nums, 0, i);
len--;
maxHeapify(nums, 0, len);
}
}
private void buildMaxHeap(int [] nums, int len)
{
for(int i = (int) (len / 2) ; i >= 0; i--)
{
maxHeapify(nums, i, len);
}
}
private void maxHeapify(int [] nums, int i, int len)
{
int left = 2 * i + 1;
int right = 2 * i + 2;
int largest = i;
if(left < len && nums[left] > nums[largest])
{
largest = left;
}
if(right < len && nums[right] > nums[largest])
{
largest = right;
}
if(largest != i)
{
swap(nums, i, largest);
maxHeapify(nums, largest, len);
}
}
private void swap(int [] nums, int i, int largest)
{
int temp = nums[i];
nums[i] = nums[largest];
nums[largest] = temp;
}
6、希尔排序
插入排序高效的实现,将待排序列分为若干子序列,分别进行插入排序,整个序列基本有序时再对进行一次直接插入排序。子序列不是简单的逐段分割,而是彼此相隔某个增量。
没有快速排序快,但是比时间复杂度为O(n2)的算法快,空间复杂度O(1),不稳定排序。
void shellSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
int len = nums.length;
int gap = 1;
while(gap < len)
{
gap = gap * 3 + 1;
}
while(gap > 0)
{
for(int i = gap; i < len; i++)
{
int j = i - gap;
int temp = nums[i];
while(j >= 0 && nums[j] > temp)
{
nums[j + gap] = nums[j];
j -= gap;
}
nums[j + gap] = temp;
}
gap = (int)(gap / 3);
}
}
7、归并排序
递归分治,先把问题划分为子问题,然后合并结果,时间复杂度为O(nlogn),空间复杂度O(n),稳定排序。
void mergeSort(int [] nums, int left, int right)
{
if(left >= right)
return;
int mid = (left + right) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
merge(nums, left, mid + 1, right);
}
private void merge(int [] nums, int left, int mid, int right)
{
int [] lArr = new int[mid - left];
int [] rArr = new int[right - mid + 1];
for(int i = left; i < mid; i++)
{
lArr[i - left] = nums[i];
}
for(int i = mid; i <= right; i++)
{
rArr[i - mid] = nums[i];
}
int i = lArr.length - 1;
int j = rArr.length - 1;
int k = right;
while(i >= 0 && j >= 0)
{
if(lArr[i] > rArr[j])
{
nums[k--] = lArr[i--];
}
else
{
nums[k--] = rArr[j--];
}
}
while(i >= 0)
{
nums[k--] = lArr[i--];
}
while(j >= 0)
{
nums[k--] = rArr[j--];
}
}
8、计数排序
基本思想是把待排序的数减去最小数作为数组的下标,统计每个数的个数,然后依次输出。需要较多的辅助空间,要求数在一定的范围内,时间复杂度为O(n)。
void countSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
int min = Integer.MIN_VALUE;
int max = Integer.MAX_VALUE;
for(int i = 0; i < nums.length; i++)
{
min = Math.min(nums[i], min);
max = Math.max(nums[i], max);
}
int [] helper = new int[max - min + 1];
for(int i = 0; i < nums.length; i++)
{
helper[nums[i] - min]++;
}
int index = 0;
for(int i = 0; i < helper.length; i++)
{
while(helper[i] > 0)
{
nums[index++] = i + min;
helper[i]--;
}
}
}
9、桶排序
基本思想是把数据放到n个桶内,对桶内数据进行排序,遍历桶依次取出桶中元素。
void bucketSort(int [] nums)
{
if(nums == null || nums.length < 2)
return;
int min = Integer.MIN_VALUE;
int max = Integer.MAX_VALUE;
for(int i = 0; i < nums.length; i++)
{
min = Math.min(min, nums[i]);
max = Math.max(max, nums[i]);
}
int bucketNum = (max - min) / nums.length + 1;
ArrayList<ArrayList<Integer>> bucket = new ArrayList<>(bucketNum);
for(int i = 0; i < bucketNum; i++)
{
bucket.add(new ArrayList<Integer>());
}
for(int i = 0; i < nums.length; i++)
{
int num = (nums[i] - min) / nums.length;
bucket.get(num).add(nums[i]);
}
for(int i = 0; i < bucket.size(); i++)
{
Collections.sort(bucket.get(i));
}
int index = 0;
for(int i = 0; i < bucket.size(); i++)
{
for(int j = 0; j < bucket.get(i).size(); j++)
{
nums[index++] = bucket.get(i).get(j);
}
}
}
10、基数排序
利用多关键字对单逻辑关键字排序。
11、二分插入排序
跟二分查找时间复杂度相同,时间复杂度最好为O(nlogn),最差为O(n2)