Sorting
1. Bubble Sort(冒泡排序)
- 从第一位开始和其下一位进行比较,如果前者大,则交换位置,这样一轮下来,最大值就在最末尾
- 第二轮循环范围从第一位至倒数第二位,依次类推
冒泡排序可以优化,即设置一个标志位,用来标记每轮循环是否有发生至少一次位置交换,如果一次位置交换也没有发生,说明排序已经完成,可以终止排序过程。
bubbleSort.cpp
#include<cstdio>
void shortBubbleSort(int list[], int len) {
bool ordered = true;
int passNum = len-1;
while(passNum>0 && ordered) {
ordered = false;
for(int j=0; j<passNum; j++) {
if(list[j]>list[j+1]) {
ordered = true;
int temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
}
}
passNum--;
}
}
int main(){
int list[]={5, 2, 7, 11, 89, 45, 32, 12, 56};
int len=sizeof(list)/sizeof(list[0]);
shortBubbleSort(list, len);
for(int n=0; n<len; n++) {
printf("%d ", list[n]);
}
return 0;
}
go版
package main
import "fmt"
func bubbleSort(list []int) {
passNum := len(list) - 1
ordered := true
for passNum > 0 && ordered {
ordered = false
for j := 0; j < passNum; j++ {
if list[j] > list[j+1] {
temp := list[j]
list[j] = list[j+1]
list[j+1] = temp
ordered = true
}
}
passNum--
}
}
func main() {
list := []int{10, 4, 7, 3, 5, 9, 8, 2, 1, 6}
bubbleSort(list)
fmt.Println(list)
}
由于两层循环,可知冒泡排序的时间复杂度为O(n2)
2. Selection Sort(选择排序)
选择排序和冒泡排序的思路基本一样,只是它每一轮循环只记录下最大值的位置,然后交换最大值和末尾的位置,即每一轮循环结束才发生依次位置交换,而不像冒泡排序每次比较都可能发生交换。
selectSort.cpp
#include<cstdio>
void selectSort(int list[], int len) {
for(int i=len-1; i>=0; i--) {
int maxIndex = 0;
for(int j=1; j<=i; j++) {
if(list[j]>list[maxIndex]) {
maxIndex = j;
}
}
int temp = list[i];
list[i] = list[maxIndex];
list[maxIndex] = temp;
}
}
int main() {
int list[]={5, 2, 7, 11, 89, 45, 32, 12, 56};
int len=sizeof(list)/sizeof(list[0]);
selectSort(list, len);
for(int n=0; n<len; n++) {
printf("%d ", list[n]);
}
return 0;
}
go版
package main
import "fmt"
func selectSort(list []int) {
for i := len(list) - 1; i >= 0; i-- {
maxIndex := 0
for j := 1; j <= i; j++ {
if list[j] > list[maxIndex] {
maxIndex = j
}
}
temp := list[maxIndex]
list[maxIndex] = list[i]
list[i] = temp
}
}
func main() {
list := []int{10, 4, 7, 3, 5, 9, 8, 2, 1, 6}
selectSort(list)
fmt.Println(list)
}
同样两层循环,时间复杂度是O(n2)
3. Insertion Sort(插入排序)
插入排序的思想是假设有一个已经排好序的队列(升序),然后来了一待排序的值,先排在队尾,然后从其前一个值开始比较,如果前一个值大,则把前一个值往后移一位,直至不满足前一个值大或到了队首,此时的位置存放该待排序值。
插入排序把队列的第一个元素看作已排好序的队列,然后执行前面的操作。
insertSort.cpp
#include<cstdio>
void insertSort(int list[], int len) {
for(int i=1; i<len; i++) {
int pos = i;
int temp = list[pos];
while(pos>0 && list[pos-1]>temp) {
list[pos] = list[pos-1];
pos--;
}
list[pos] = temp;
}
}
int main() {
int list[]={5, 2, 7, 11, 89, 45, 32, 12, 56};
int len=sizeof(list)/sizeof(list[0]);
insertSort(list, len);
for(int n=0; n<len; n++) {
printf("%d ", list[n]);
}
return 0;
}
go版
package main
import "fmt"
func insertSort(list []int) {
for i := 1; i < len(list); i++ {
pos := i
temp := list[pos]
for pos > 0 && list[pos-1] > temp {
list[pos] = list[pos-1]
pos--
}
list[pos] = temp
}
}
func main() {
list := []int{10, 4, 7, 3, 5, 9, 8, 2, 1, 6}
insertSort(list)
fmt.Println(list)
}
插入排序的时间复杂度也是O(n2)
4. Shell Sort(希尔排序)
希尔排序是在插入排序的基础上,将原始队列分成几个小的队列分别进行插入排序,来提高排序的速度。
shellSort.cpp
#include<cstdio>
void gapInsertSort(int list[], int len, int start, int gap) {
for(int i=start+gap; i<len; i+=gap) {
int pos = i;
int temp = list[pos];
while(pos>=gap && list[pos-gap]>temp) {
list[pos] = list[pos-gap];
pos-=gap;
}
list[pos] = temp;
}
}
void shellSort(int list[], int len) {
int sublistCount = len/2;
while(sublistCount>0) {
for(int i=0; i<sublistCount; i++) {
gapInsertSort(list, len, i, sublistCount);
}
sublistCount /= 2;
}
}
int main() {
int list[]={5, 2, 7, 11, 89, 45, 32, 12, 56};
int len=sizeof(list)/sizeof(list[0]);
shellSort(list, len);
for(int n=0; n<len; n++) {
printf("%d ", list[n]);
}
return 0;
}
go版
package main
import "fmt"
func gapInsertSort(list []int, start int, gap int) {
for i := start + gap; i < len(list); i += gap {
pos := i
temp := list[pos]
for pos >= gap && list[pos-gap] > temp {
list[pos] = list[pos-gap]
pos -= gap
}
list[pos] = temp
}
}
func shellSort(list []int) {
sublistCount := len(list) / 2
for sublistCount > 0 {
for i := 0; i < sublistCount; i++ {
gapInsertSort(list, i, sublistCount)
}
sublistCount /= 2
}
}
func main() {
list := []int{10, 4, 7, 3, 5, 9, 8, 2, 1, 6}
shellSort(list)
fmt.Println(list)
}
希尔排序通过排序递增的子序列提升了插入排序的效率,时间复杂度介于O(n)和O(n2)
5. Merge Sort(归并排序)
归并排序采用的是分治法思想,它是一种递归的算法,持续对半分割队列,直至队列为空或只含有一个元素,然后进行合并操作。
mergeSort.cpp
#include<cstdio>
const int maxn=100;
// 排序两个有序序列
void mergeTwoList(int l1[], int len1, int l2[], int len2, int l3[]) {
int i=0, j=0, index=0;
while(i<len1 && j<len2) {
if(l1[i]>=l2[j]) {
l3[index++]=l2[j++];
} else {
l3[index++]=l1[i++];
}
}
while(i<len1) {
l3[index++]=l1[i++];
}
while(j<len2) {
l3[index++]=l2[j++];
}
}
void merge(int list[], int left1, int right1, int left2, int right2) {
int temp[maxn], index=0;
int i=left1, j=left2;
while(i<=right1 && j<=right2) {
if(list[i]>=list[j]) {
temp[index++]=list[j++];
} else {
temp[index++]=list[i++];
}
}
while(i<=right1) {
temp[index++]=list[i++];
}
while(j<=right2) {
temp[index++]=list[j++];
}
for(int n=0; n<index; n++) {
list[left1+n]=temp[n];
}
}
void mergeSort(int list[], int left, int right) {
if(left<right) {
int mid=(left+right)/2;
mergeSort(list, left, mid);
mergeSort(list, mid+1, right);
merge(list, left, mid, mid+1, right);
}
}
int main() {
int list[]={5, 7, 2, 4, 8, 1, 3, 10, 9, 6};
int len = sizeof(list)/sizeof(list[0]);
mergeSort(list, 0, len-1);
for(int n=0; n<len; n++) {
printf("%d ", list[n]);
}
return 0;
}
go版
package main
import "fmt"
func merge(list []int, left1 int, right1 int, left2 int, right2 int) {
temp := []int{}
i := left1
j := left2
for i <= right1 && j <= right2 {
if list[i] >= list[j] {
temp = append(temp, list[j])
j++
} else {
temp = append(temp, list[i])
i++
}
}
for i <= right1 {
temp = append(temp, list[i])
i++
}
for j <= right2 {
temp = append(temp, list[j])
j++
}
n := len(temp)
for k := 0; k < n; k++ {
list[left1+k] = temp[k]
}
}
func mergeSort(list []int, left int, right int) {
if left < right {
mid := (left + right) / 2
mergeSort(list, left, mid)
mergeSort(list, mid+1, right)
merge(list, left, mid, mid+1, right)
}
}
func main() {
list := []int{10, 4, 7, 3, 5, 9, 8, 2, 1, 6}
mergeSort(list, 0, len(list)-1)
fmt.Println(list)
}
归并排序的时间复杂度是O(nlogn),但它需要额外的空间用于排序过程。
6. Quick Sort(快速排序)
快速排序的思想是在队列中随机选择一个值,然后队列中比该值小的值排在其左边,比该值大的值排在其右边,对于左右两边的子队列重复使用这种方法,最终完成排序。
quickSort.cpp
#include<cstdio>
int patition(int list[], int left, int right) {
int temp = list[left];
while(left<right) {
while(left<right && list[right]>=temp) right--;
list[left] = list[right];
while(left<right && list[left]<temp) left++;
list[right] = list[left];
}
list[left] = temp;
for(int n=0; n<10; n++) {
printf("%d ", list[n]);
}
printf("\n");
return left;
}
void quickSort(int list[], int left, int right) {
if(left<right) {
int pos = patition(list, left, right);
quickSort(list, left, pos-1);
quickSort(list, pos+1, right);
}
}
int main() {
int list[]={14,17,13,15,19,10,3,16,9,12};
int len = sizeof(list)/sizeof(list[0]);
quickSort(list, 0, len-1);
for(int n=0; n<len; n++) {
printf("%d ", list[n]);
}
return 0;
}
go版
package main
import "fmt"
func patition(list []int, left int, right int) int {
temp := list[left]
for left < right {
for left < right && list[right] >= temp {
right--
}
list[left] = list[right]
for left < right && list[left] < temp {
left++
}
list[right] = list[left]
}
list[left] = temp
return left
}
func quickSort(list []int, left int, right int) {
if left < right {
pos := patition(list, left, right)
quickSort(list, left, pos-1)
quickSort(list, pos+1, right)
}
}
func main() {
list := []int{6, 4, 7, 3, 5, 10, 8, 2, 1, 9}
quickSort(list, 0, len(list)-1)
fmt.Println(list)
}
快速排序的时间复杂度是O(nlogn),但当分割点不靠近列表中间时,退化到O(n2)
总体思路是选取一个元素,比该元素大的位于其右边,小的置于其左边。
步骤:
- 采用左右两边同时遍历的方式
- 初始左边索引left=0,右边索引right=len-1
- 左边第一个值赋值给temp
- 然后从右边索引开始递减遍历与temp比较,s[right]大或等于,继续遍历;s[right]小,则将此时s[right]赋值给s[left]。然后从左边开始递增遍历比较,如果s[left]小,则继续遍历;s[left]大,则将此时s[left]赋值给s[right]。最后将temp赋值给此时s[left]。这样原数组第一个值位置确认了,记为pos。
- 然后把[0, pos-1]和[pos+1, right]作为两组数组按照步骤1到4进行,left>=right结束。
Searching
- 顺序查找对于有序和无序列表的时间复杂度都是O(n)
- 有序列表使用二分查找的时间复杂度是O(logn)
binarySearch.cpp
#include<cstdio>
// 有序序列元素不重复
int binary_search(int list[], int left, int right, int num) {
int mid;
while(left<=right) {
mid = (left+right)/2;
if(list[mid]==num) {
return mid;
}
else if(list[mid]>num) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
return -1;
}
// 有序序列元素可重复, 查找第一个
int lower_bound(int list[], int left, int right, int num) {
int mid;
while(left<right) {
mid = (left+right)/2;
if(list[mid]>=num) {
right = mid;
}
else {
left = mid+1;
}
}
if(list[left]==num) {
return left;
}
else {
return -1;
};
}
int main() {
const int LEN = 5;
int list[LEN]={0};
for(int i=0; i<LEN; i++) {
scanf("%d", &list[i]);
}
int left=0, right=LEN-1, num=9;
int index = binary_search(list, left, right, num);
int lower = lower_bound(list, left, right, num);
printf("%d %d\n", index, lower);
return 0;
}
go版
package main
import "fmt"
func binarySearch(list []int, num int) int {
left := 0
right := len(list) - 1
for left <= right {
mid := (left + right) / 2
if list[mid] == num {
return mid
} else if list[mid] > num {
right = mid - 1
} else {
left = mid + 1
}
}
return -1
}
func main() {
list := []int{1, 2, 3, 5, 7, 9}
index := binarySearch(list, 4)
fmt.Println(index)
}
网友评论