冒泡排序
public class Main{
public void Sort(int[] nums) {
int i, j;
for(i = nums.length - 1; i > 0; i--){
for(j = 0; j < i; j++){
if(nums[j+1] < nums[j]){
nums[j+1] ^= nums[j];
nums[j] ^= nums[j+1];
nums[j+1] ^= nums[j];
}
}
}
}
}
每一次循环是比较相邻两个数的大小,小的在前,大的在后,一趟后最后一个数是最大的数。
无论最好还是最坏情况下时间复杂度都是
O
(
N
2
)
O(N^2)
O(N2),是一种比较低效率的排序方法。
选择排序
public class Main{
public void Sort(int[] nums) {
int i, j, min;
for(i = 0; i < nums.length; i++){
min = i;
for(j = i + 1; j < nums.length; j++){
if(nums[j] < nums[min]){
min = j;
}
}
if(min != i) {
nums[min] ^= nums[i];
nums[i] ^= nums[min];
nums[min] ^= nums[i];
}
}
}
}
插入排序
插入排序的思想是假设已经排好了前面一部分,再将后面的元素一个一个插入到排好的序列中,并保持有序。
public class Main{
public void insertSort(int[] nums) {
int i, j;
for(i = 1; i < nums.length; i++){
for(j = i; j > 0; j--){
if(nums[j] < nums[j-1]){
nums[j] ^= nums[j-1];
nums[j-1] ^= nums[j];
nums[j] ^= nums[j-1];
}else{
break;
}
}
}
}
}
插入排序的时间复杂度不稳定,跟原序列的排序情况有关,如果原序列已经是有序(和目标顺序一致)的,那么时间复杂度是 O ( N ) O(N) O(N),而如果是逆序的,则时间复杂度是 O ( N 2 ) O(N^2) O(N2)。
归并排序
归并排序的思路是一半一半排序,假设左右两边都已经是有序的了,那么再按大小顺序插入一个新的数组里即可,需要 N N N 次操作,而利用递归来使左右两边达到有序需要 l o g 2 n log_2n log2n 次,因此总的时间复杂度是 O ( N l o g N ) O(NlogN) O(NlogN),又因为需要一个辅助的数组,空间复杂度是 O ( N ) O(N) O(N)。
public void mergeSort(int[] nums) {
int[] help;
help = new int[nums.length];
mergeSortHelper(nums, help, 0, nums.length);
}
private void mergeSortHelper(int[] nums, int[] help, int start, int end) {
int i, j, k, mid;
if(start + 1 == end)
return;
mid= (start + end) / 2;
mergeSortHelper(nums, help, start, mid);
mergeSortHelper(nums, help, mid, end);
i = start;
j = mid;
k = start;
while(i < mid && j < end){
if(nums[i] < nums[j]){
help[k++] = nums[i];
i++;
}else {
help[k++] = nums[j];
j++;
}
}
while(i < mid){
help[k++] = nums[i++];
}
while(j < end){
help[k++] = nums[j++];
}
for(k = start; k < end; k++){
nums[k] = help[k];
}
}
快速排序
快排的大致思路是找到一个中间数,使这个数左边的都小于等于它,右边的数都大于等于它,然后接着递归处理两边的部分。
快排的时间复杂度和原数组里的顺序有关,平均复杂度为
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN),当原数组是有序的时候,时间复杂度达到最差,为
O
(
N
2
)
O(N^2)
O(N2)
public void quickSort(int[] nums) {
quickSortHelper(nums,0, nums.length);
}
private void quickSortHelper(int[] nums, int start, int end) {
int i, j, m, t;
i = start;
j = end - 1;
while(i < j){
m = i;
while(i < j && nums[j] >= nums[m]){
j--;
}
while(i < j && nums[i] <= nums[m]){
i++;
}
t = nums[m];
nums[m] = nums[j];
nums[j] = nums[i];
nums[i] = t;
}
if(start < i)
quickSortHelper(nums, start, i);
if(i < end)
quickSortHelper(nums, i + 1, end);
}
非递归版本
public void quickSort(int[] nums) {
int i, j, m, t;
int[] range;
Stack<int[]> stack;
stack = new Stack<>();
stack.push(new int[]{0, nums.length - 1});
while(!stack.isEmpty()){
range = stack.pop();
i = range[0];
j = range[1];
while(i < j){
m = i;
while(i < j && nums[j] >= nums[m])
j--;
while(i < j && nums[i] <= nums[m])
i++;
t = nums[m];
nums[m] = nums[j];
nums[j] = nums[i];
nums[i] = t;
}
if(range[0] < i - 1)
stack.push(new int[]{range[0], i - 1});
if(i + 1 < range[1])
stack.push(new int[]{i + 1, range[1]});
}
}
堆排序
由于大根堆具有堆顶是最大值的性质,因此可以根据堆的插入(向上重排)和删除(向下重排)来进行排序。
堆的插入和删除都涉及到堆的重排,时间复杂度是
O
(
l
o
g
N
)
O(logN)
O(logN),需要
N
N
N 次,因此时间复杂度是
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)。
class Solution {
public void heapSort(int[] nums) {
int i, j, next;
//大根堆
for(i = 1; i < nums.length; i++){
next = i;
//向上重排
while(next != 0){
j = (next - 1) / 2;
if(nums[next] > nums[j]){
nums[next] ^= nums[j];
nums[j] ^= nums[next];
nums[next] ^= nums[j];
next = j;
}else {
break;
}
}
}
//排序
for(i = nums.length - 1; i > 0; i--){
nums[i] ^= nums[0];
nums[0] ^= nums[i];
nums[i] ^= nums[0];
//向下重排
next = 0;
while(next < i){
j = 2 * next + 1;
if(j + 1 < i){
j += nums[j] > nums[j+1] ? 0 : 1;
}
if(j < i){
nums[next] ^= nums[j];
nums[j] ^= nums[next];
nums[next] ^= nums[j];
}
next = j;
}
}
}
}