实验2 利用蛮力法、减治法和分治法解决排序问题
一、实验目的
1. 掌握蛮力法、减治法和分治法的思想与实现。
2. 掌握利用利用蛮力法、减治法和分治法解决排序问题。
3. 分析核心代码的时间复杂度和空间复杂度。
二、实验内容和要求
基于蛮力法(选择排序、冒泡排序)、减治法(插入排序)和分治法(合并排序、快速排序)的思想分别编写一个排序算法。
【选择排序函数原型及功能说明】
选择排序函数原型:
SelectSort(A[0..n-1])
//该算法用选择排序对给定的数组排序
// 输入:一个可排序数组A[0..n-1]
// 输出:升序排列的数组A[0..n-1]
for i ← 0 to n-2 do
min ← i
for j ← i+1 to n-1 do
if A[j] < A[min] min ← j
swap A[j] and A[min]
功能说明:
选择排序开始的时候,我们扫描整个列表,找到它的最小元素,然后和第一个元素交换,将最小元素放到它在有序表中的最终位置上。然后我们从第二个元素开始扫描列表,找到最后n-1个元素中的最小元素,再和第二个元素交换位置,把第二小的元素放在它的最终位置上。一般来说,在对该列表做第i遍扫描的时候(i的值从0到n-2),该算法在最后n-i个元素中寻找最小元素,然后拿它和Ai交换。
【核心函数实现代码及时间复杂度与空间复杂度分析】
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i; // 用来记录最小值的索引位置,默认值为i
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j; // 遍历 i+1~length 的值,找到其中最小值的位置
}
}
// 交换当前索引 i 和最小值索引 minIndex 两处的值
if (i != minIndex) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
// 执行完一次循环,当前索引 i 处的值为最小值,直到循环结束即可完成排序
}
}
【冒泡排序函数原型及功能说明】
冒泡排序函数原型:
ubbleSort(A[0..n-1])
//该算法用冒泡排序对数组A[0..n-1]进行排序
// 输入:一个可排序数组A[0..n- 1]
//输出:非降序排列的数组A[0..n- 1]
for i ← 0 to n-2 do
for j ← 0 to n-2-i do
if A[j+1] < A[j]
swap A[j] and A[j+1]
功能说明:
蛮力法在排序问题上还有另一个应用,它比较表中的相邻元素,如果它们是逆序的话就交换它们的位置。重复多次以后,最终,最大的元素就“沉到”列表的最后一个位置。第二遍操作将第二大的元素沉下去。这样一直做,直到n-1遍以后,该列表就排好序了。
【核心函数实现代码及时间复杂度与空间复杂度分析】
public static void bubbleSort(int[] arr){
//冒泡排序的时间复杂度为O(n*n)
int temp = 0;//临时变量
for (int j = 0; j < arr.length - 1; j++) {
for (int i = 0; i < arr.length-1 -j ; i++) {
if (arr[i] > arr[i+1]){
//三角交换
temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
}
【插入排序函数原型及功能说明】
插入排序函数原型:
InsertionSort(A[0..n-1])
//用插入排序对给定数组排序
//输入:n个可排序元素构成的一个数组A[0..n-1]
// 输出:非降序排列的数组A[0..n- 1]
for i ← 1 to n-1 do
v ← A[i]
j ← i-1
while j ≥ 0 and A[j] > v do
A[j+1] ← A[j]
j ← j-1
A[j+1] ← v
功能说明:
从右到左扫描这个有序的子数组,直到遇到第一个小于等于A[n-1]的元素,然后把A[n-1]插在该元素的后面。插入排序是基于递归思想的,从底至上地实现这个算法,使用迭代,效率会更高。从A[1]开始,到A[n-1]为止,A[i]被插在数组的前i个有序元素中的适当位置上(但是,和选择排序不同,这个位置一般来说并不是它们的最终位置)。
【核心函数实现代码及时间复杂度与空间复杂度分析】
public static void insertSort(int[] arr) {
int i,j,temp;
for(i=1;i<arr.length;i++) {
temp=arr[i];
for(j=i-1;j>=0;j--) {
if(temp>arr[j]) {
break;
}else {
arr[j+1]=arr[j];
}
}
arr[j+1]=temp;
}
}
【合并排序函数原型及功能说明】
插入排序函数原型:
Mergesort(A[0..n - 1])
//递归调用mergesort来对数组4[0..n-1]排序//输入:一个可排序数组A[0..n-1]
//输出:非降序排列的数组A[0..n-1]
if n>1
copy A[0..⌊n/2⌋-1] to B[0..⌊n/2⌋-1]
copy A[⌊n/2⌋..n-1] to C[0.. ⌈n/2⌉-1]
Mergesort(B[0..⌊n/2⌋-1)
Mergesort(C[0..⌈n/2⌉-1])
Merge(B,C,A)
Merge(B[0..p- 1], C[0..q-1],A[0..p+q-1])
//将两个有序数组合并为一个有序数组
//输入:两个有序数组B[0..p-1]和C[0..q-1]
//输出:A[0..p+q-1]中已经有序存放了B和C中的元素i <—0; j <-0; k <- 0
while i < p and j < q do
ifB[i] ≤ C[j]
A[k] ← B[i];i ← i+1
else A[k] ← C[j];j ← j+1
k ← k+1
if i = p
copy C[j..q-1] to A[k..p+q-1]
else copy B[i..p-1] to A[k..p+q-1]
功能说明:
合并排序是成功应用分治技术的一个完美例子。对于一个需要排序的数组A[0..n-1],合并排序把它一分为二:A[0..⌊n/2⌋-1]和A[⌊n/2⌋ ..n-1],并对每个子数组递归排序,然后把这两个排好序的子数组合并为一个有序数组。
【核心函数实现代码及时间复杂度与空间复杂度分析】
public static void mergeSort(int[] a, int n) {
if (n < 2) {
return;
}
int mid = n / 2;
int[] l = new int[mid];
int[] r = new int[n - mid];
for (int i = 0; i < mid; i++) {
l[i] = a[i];
}
for (int i = mid; i < n; i++) {
r[i - mid] = a[i];
}
mergeSort(l, mid);
mergeSort(r, n - mid);
merge(a, l, r, mid, n - mid);
}
public static void merge(
int[] a, int[] l, int[] r, int left, int right) {
int i = 0, j = 0, k = 0;
while (i < left && j < right) {
if (l[i] <= r[j]) {
a[k++] = l[i++];
}
else {
a[k++] = r[j++];
}
}
while (i < left) {
a[k++] = l[i++];
}
while (j < right) {
a[k++] = r[j++];
}
}
【快速排序函数原型及功能说明】
函数原型:
Quicksort(A[l..r])
//用Quicksort对子数组排序
//输入:数组A[0..n-1]中的子数组A[l..r],由左右下标l和r定义
//输出:非降序排列的子数组A[1..r]
if l<r
s <- Partition (A[l..r])//s是分裂位置
Quicksort(A[l..s-1])
Quicksort(A[s+1..r])
功能说明:
建立了一个划分以后,A[s]已经位于它在有序数组中的最终位置,接下来继续对A[s]前和A[s]后的子数组分别进行排序(例如,使用同样的方法)。它与合并排序的不同之处在于:在合并排序算法中,将问题划分成两个子问题是很快的,算法的主要工作在于合并子问题的解;而在快速排序中,算法的主要工作在于划分阶段,而不需要再去合并子问题的解了。
【核心函数实现代码及时间复杂度与空间复杂度分析】
public static void quickSort(int[] arr,int low,int high){
int i,j,temp,t;
if(low>high){
return;
}
i=low;
j=high;
//temp就是基准位
temp = arr[low];
while (i<j) {
//先看右边,依次往左递减
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,依次往右递增
while (temp>=arr[i]&&i<j) {
i++;
}
//如果满足条件则交换
if (i<j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
【程序运算结果截图】
三、实验总结
本次实验为本学期第二次实验,实验主题为利用蛮力法、减治法和分治法解决排序问题。任务分别为基于蛮力法(选择排序、冒泡排序)、减治法(插入排序)和分治法(合并排序、快速排序)的思想编写一个排序算法。通过本次实验,我学习到了蛮力法、减治法和分治法的思想与实现,学到可如何利用蛮力法、减治法和分治法解决排序问题以及学到了如何分析核心代码的时间复杂度和空间复杂度,为今后得程序开发奠定基础,更有利于今后对算法设计与分析这一门课程的学习。
四、优化及改进(选做)
【提出你觉得解决这个问题更好的算法,并加以说明】