一、冒泡排序
平均时间复杂度: O(n^2)
思想:
- 1、将第一个数与后面的所有数进行比较,如果后面的数比第一个大(或者小),则将这两个数的位置进行交换,这样就找出了最小(或者最大)的数,并将其放在数组中的第一个位置;
- 2、将第二个数与后面的所有数进行比较,与第一步相同,找出第二小(或者第二大)的数,并放在数组中第二个位置;
- 3、依次循环,直到将倒数第二个数与倒数第一个数进行比较。
代码实现:
void BubbleSort(int arr[], int length)
{
for(int i = 0; i < length - 1; i++)
{
for(int j = 0; j < length - i - 1; j++)
{
if(arr[j + 1] < arr[j]) //如果前面的数小于后面的数,则交换两个数的位置,即找出最大的那个数,将其放在数据的第一个位置
{
swap(arr[j + 1], arr[j]);
}
}
}
}
二、选择排序
平均时间复杂度: O(n^2)
思路:
- 1、将第一个数假定为最小的值 ,并记录下标为min/max
- 2、跟后面的数与arr[min/max]进行比较,如果后面的数有比其小/大,更新min/max
- 3、依次循环,一共循环n - 1 次
代码实现:
void SelectSort(int arr[], int length)
{
for(int i = 0; i < length - 1; i++)
{
int min = i;
for(int j = i + 1; j < length; j++)
{
if(arr[j] < arr[min])
{
min = j;
}
}
if(min != i)
{
swap(arr[i], arr[min]);
}
}
}
三、插入排序
时间复杂度: O(n^2)
思路:
- 1、元素被分为有序区和无序区两部分。最初有序区只有一个元素。每次从无序区中选择一个元素,插入到有序区的位置,直到无序区变空
代码示例:
void InsertSort(int arr[], int length)
{
for(int i = 1; i < length; i++)
{
if(arr[i] < arr[i - 1])
{
int temp = arr[i];
int j;
for(j = i - 1; j >=0 && arr[j] > temp; j--)
{
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
}
}
四、快速排序
时间复杂度: O(nlogn)
思路:
- 分治法 + 挖坑填数
- 使用递归的方式
代码示例:
void QuickSort(int arr[], int start, int end)
{
int i = start;//快速排序起始位置
int j = end; //快速排序终止位置
int temp = arr[start]; //基准数
if(i < j)
{
while(i < j)
{
//找右半边比基准数小的
while(i < j && arr[j] > temp)
{
j--;
}
//填数
if(i < j)
{
arr[i] = arr[j];
i++;
}
//找左半边比基准数大的
while(i < j && arr[i] < temp)
{
i++;
}
//填数
if(i < j)
{
arr[j] = arr[i];
j--;
}
}
//把基准数填入相应的坑当中
arr[i] = temp;
//将左半边进行快速排序
QuickSort(arr, start, i - 1);
//将右半边进行快速排序
QuickSort(arr, i + 1, end);
}
}
五、归并排序
时间复杂度: O(nlogn)
思路:
- 将两个有序的序列合并成一个有序序列, 使用递归的方式
- 1、将原数据进行分组,直至分成一个元素
- 2、将分组好的数据进行合并
- 3、需要辅助空间
代码示例:
//合并两个有序序列
void Merge(int arr[], int start, int end, int mid, int temp[])
{
int i_start = start;
int i_end = mid;
int j_start = mid + 1;
int j_end = end;
//合并两个有序序列
int length = 0;
while(i_start <= i_end && j_start <= j_end)
{
if(arr[i_start] < arr[j_start])
{
temp[length++] = arr[i_start++];
}
else
{
temp[length++] = arr[j_start++];
}
}
//如果有其中的一个序列不为空,则将这个序列剩下的元素放到辅助空间中去
while(i_start <= i_end)
{
temp[length++] = arr[i_start++];
}
while(j_start <= j_end)
{
temp[length++] = arr[j_start++];
}
//将辅助空间中的数据复制到原空间中
for(int i = 0; i < length; i++)
{
arr[i + start] = temp[i];
}
}
//归并排序
void MergeSort(int arr[], int start, int end, int temp[]) //temp是辅助空间,大小与原数组大小相同
{
if(start >= end)
{
return;
}
//分组
int mid= (start + end) / 2;
//将左半边的数据进行分组
MergeSort(arr, start, mid, temp);
//将右半边的数据进行分组
MergeSort(arr, mid + 1, end, temp);
//合并
Merge(arr, start, end, mid, temp);
}
六、希尔排序
时间复杂度: O(nlogn)~O(n^2)
思路:
代码实现:
void ShellSort()
{
}
七、堆排序
时间复杂度: O(nlogn)
完全二叉树:
- 对任意节点 i (非叶子节点)
左孩子节点编号:2 * i + 1
右孩子节点编号:2 * i + 2 - 对任意节点j(非根节点)
其父亲节点编号:(j - 1) / 2
大顶堆: 父节点的值大于左右孩子节点的值的完全二叉树;
小顶堆: 父节点的值小于左右孩子节点的值的完全二叉树。
思路:
- 1、将数组初始化成大顶堆、小顶堆的形式
- 2、将数组最后一个元素与堆顶元素交换,然后再将其调整成大顶堆/小顶堆的形式
- 3、循环第二步,直到调整到最后一个元素
代码示例:
//调整堆(大顶堆)
void HeapAdjust(int arr[], int index, int length)
{
int max = index;
int lchild = 2 * index + 1;
int rchild = 2 * index + 2;
//将左孩子、右孩子与当前节点的值相比较,然后把最大的那个作为父亲节点
if(lchild < length && arr[lchild] > arr[max])
{
max = lchild;
}
if(rchild < length && arr[rchild] > arr[max])
{
max = rchild;
}
if(max != index)
{
swap(arr[max], arr[index]);
HeapAdjust(arr, max, length);
}
}
//堆排序
void HeapSort(int arr[], int length)
{
//初始化堆
for(int i = length / 2 - 1; i >= 0; i--) //因为数组下标是从0开始的,所以i应该从length / 2 - 1 开始
{
HeapAdjust(arr, i, length);
}
//将堆顶元素与最后一个元素交换,再重新调整堆
for(int i = length - 1; i >= 0; i--)
{
swap(arr[0], arr[i]);
HeapAdjust(arr, 0, i);
}
}