最近,想巩固下基础,复习下排序算法,排序算法对于程序员来说并不陌生,在平常的项目中以及面试中,都会涉及排序算法,常见的排序算法有,冒泡排序,插入排序,选择排序,堆排序,计数排序,归并排序,基数排序,桶排序。 按照时间复杂度可分为三类:
冒泡排序:
冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。
js代码:
function bubbleSort(nums) {
if (nums.length <= 1){
return;
}
let flag = 0;
for (let i=0; i < nums.length; i++){
for (let j = 0; j < nums.length - i - 1; j++){
if (nums[j] > nums[j + 1]){
let temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = 1;
}
}
if (!flag){
return
}
}
}
python代码:
def bubble_sort(arr):
n = len(arr)
# 遍历所有数组元素
for i in range(n):
flag = 0
# 最后i个元素已经有序,不需要再比较
for j in range(0, n-i-1):
# 如果元素比相邻的元素大,则交换它们
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
flag = 1
if flag == 0:
break
print(arr)
t = [90, 34, 25, 12, 22, 11, 3]
bubble_sort(t)
print(t)
插入排序:将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
function insertSort(nums) {
if (nums.length <= 1){
return;
}
for (let i=1; i < nums.length; i++){
let value = nums[i];
let j = i - 1;
for (; j >= 0; j--){
if (value < nums[j]){
nums[j+1] = nums[j];
} else {
break;
}
}
nums[j+ 1] = value;
}
}
python代码
def insert_sort(arr):
n = len(arr)
# 遍历所有数组元素
for i in range(1, n):
value = arr[i]
j = i - 1
while j >= 0 and arr[j] > value:
arr[j + 1] = arr[j]
j -= 1
arr[j+1] = value
print(arr)
t = [90, 34, 25, 12, 22, 11, 3]
insert_sort(t)
print(t)
选择排序:选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
function selectSort(nums) {
if (nums.length <= 1){
return;
}
for (let i=0; i < nums.length - 1; i++){
let index = i;
for (let j = i + 1; j < nums.length; j++){
if (nums[index] > nums[j]){
index = j;
}
}
let temp = nums[i]
nums[i] = nums[index];
nums[index] = temp;
}
}
python代码:
def select_sort(arr):
n = len(arr)
# 遍历所有数组元素
for i in range(0, n):
index = i
for j in range(i+1, n):
if arr[index] > arr[j]:
index = j
arr[index], arr[i] = arr[i],arr[index]
t = [90, 34, 25, 12, 22, 11, 3]
select_sort(t)
print(t)
归并排序: 归并排序的核心思想还是蛮简单的。如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
function mergeSort(nums) {
if (nums.length <= 1){
return nums;
}
let mid = Math.floor(nums.length / 2);
let a = mergeF(nums, 0, mid);
let b = mergeF(nums, mid + 1, nums.length - 1);
let d = [];
merge(d, a , b);
return d;
}
function merge(a, b , c) {
let i = 0;
let j = 0;
while(i < b.length && j < c.length){
if (b[i] > c[j]){
a.push(c[j]);
j++;
} else {
a.push(b[i]);
i++;
}
}
for (let k = i; k < b.length; k++){
a.push(b[k]);
}
for (let k = j; k < c.length; k++){
a.push(c[k]);
}
}
function mergeF(nums, start , end) {
if (start > end){
return [];
}
if (start === end){
return [nums[start]];
}
let mid = Math.floor((start + end)/ 2);
let a = mergeF(nums, start, mid);
let b = mergeF(nums, mid + 1, end);
let d = [];
merge(d, a , b);
return d;
}
python:
def mergeSort(arr):
def merge(a, b):
c = []
i = 0
j = 0
while i < len(a) and j < len(b):
if a[i] < b[j]:
c.append(a[i])
i += 1
else:
c.append(b[j])
j += 1
while i < len(a):
c.append(a[i])
i += 1
while j < len(b):
c.append(b[j])
j += 1
return c
def mergeSortF(arr, start, end):
if start >= end:
return [arr[start]]
mid = (start + end)//2
a = mergeSortF(arr, start, mid)
b = mergeSortF(arr, mid+1, end)
return merge(a,b)
n = len(arr)
start = 0
end = n - 1
mid = (start + end)//2
a = mergeSortF(arr, start, mid)
b = mergeSortF(arr, mid+1, end)
return merge(a, b)
t = [90, 34, 25, 12, 22, 11, 3]
b = mergeSort(t)
print(b)
快排 快排的思想是这样的:如果要排序数组中下标从 p 到 r 之间的一组数据,我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。
根据分治、递归的处理思想,我们可以用递归排序下标从 p 到 q-1 之间的数据和下标从 q+1 到 r 之间的数据,直到区间缩小为 1,就说明所有的数据都有序了
function quickSort(nums) {
if (nums.length <= 1){
return nums;
}
quickPartition(nums, 0, nums.length-1);
return nums;
}
function quickPartition(nums, p, q) {
if (p >= q){
return;
}
let r = partition(nums, p, q);
console.log(r);
quickPartition(nums, p, r - 1);
quickPartition(nums, r + 1, q);
}
function partition(nums, p, q) {
let pivot = nums[q];
let i = p, j = p;
while(j < q){
if (nums[j] < pivot){
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++;
}
j++;
}
let temp = nums[i];
nums[i] = nums[q];
nums[q] = temp;
return i;
}
python:
def quickSort(arr):
def partition(arr, start, end):
j = start
value = arr[end]
for i in range(start, end):
if arr[i] < value:
arr[j], arr[i] = arr[i], arr[j]
j += 1
arr[j], arr[end] = arr[end], arr[j]
return j
def quickF(arr, start, end):
if start >= end:
return
mid = partition(arr, start, end)
quickF(arr, start, mid-1)
quickF(arr, mid+1, end)
n = len(arr)
quickF(arr, 0, n-1)
t = [90, 34, 25, 12, 22, 11,3]
quickSort(t)
print(t)
桶排序
顾名思义,会用到“桶”,核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。
计数排序
计数排序其实是桶排序的一种特殊情况。当要排序的 n 个数据,所处的范围并不大的时候,比如最大值是 k,我们就可以把数据划分成 k 个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。
基数排序
基数排序对要排序的数据是有要求的,需要可以分割出独立的“位”来比较,而且位之间有递进的关系,如果 a 数据的高位比 b 数据大,那剩下的低位就不用比较了。除此之外,每一位的数据范围不能太大,要可以用线性排序算法来排序,否则,基数排序的时间复杂度就无法做到 O(n) 了。