第八章 排序和查找
排序的基本介绍
排序是将一组数据,依指定的顺序进行过排列的过程。
排序的分类
- 内部排序:指将需要处理的所有数据都加载到内部存储器中排序。包括:交换式排序、选择式排序和插入式排序;
- 外部排序:数据量过大,无法全部加载到内存中,需要借助外部存储惊醒排序。包括:合并排序法和直接合并排序法。
交换式排序法
交换式排序属于内部排序法,是运用数据值比较后,依判断规则对数据位置进行交换,以达到排序目的。
- 冒泡排序法(Bubble sort):基本思想:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素排序码,若发现逆序则交换,是排序码较小的元素逐渐从后部移向前部(从下标较大的单元移向较小的单元)
冒泡排序的规则:
(1)一共会经过arr.length-1次的轮数的比较,每一轮确定一个数的比较
(2)每一轮的比较次数在逐渐的减少
(3)当发现前面的一个数比后面的一个数大的时候,就进行了交换
package main
import (
"fmt"
)
// BubbleSort 冒泡排序
func BubbleSort(arr *[5]int) {
fmt.Println("排序前arr = ", *arr)
//定义一个临时变量,用于交换
temp := 0
for i := 0; i < len(*arr)-1; i++ {
for j := 0; j < len(*arr)-1-i; j++ {
if (*arr)[j] > (*arr)[j+1] { //从小到大排序 从大到小排序:if (*arr)[j] < (*arr)[j+1]
temp = (*arr)[j]
(*arr)[j] = (*arr)[j+1]
(*arr)[j+1] = temp
}
}
}
fmt.Println("冒泡排序后arr = ", *arr)
}
func main() {
//定义一个数组
arr := [5]int{24, 69, 80, 57, 13}
BubbleSort(&arr)
fmt.Println("main arr = ", arr) //有序的
}
- 快速排序法(Quick sort)
查找
介绍
在Golang中,常用的查找方式有:
- 顺序查找
package main
import "fmt"
func main() {
names := [4]string{"北京", "上海", "长春", "杭州"}
var cityName = ""
fmt.Println("请输入要查找的城市名称")
fmt.Scanln(&cityName)
//顺序查找:第一种方式
for i := 0; i < len(names); i++ {
if cityName == names[i] {
fmt.Printf("找到%v ,下标是%v\n", cityName, i)
break
} else if i == (len(names) - 1) {
fmt.Printf("没有找到%v\n", cityName)
}
}
//顺序查找:第二种方式
index := -1
for i := 0; i < len(names); i++ {
if cityName == names[i] {
index = i //将找到的值对应的下标赋值给index
break
}
}
if index != -1 {
fmt.Printf("找到%v ,下标是%v\n", cityName, index)
} else {
fmt.Println("没有找到", cityName)
}
}
- 二分查找(该数组是有序的)
二分查找的思路 - arr是一个有序数组,并且是从小到大排序
- 先知道中间的下标middle=(leftIndex+rightIndex)/2,然后让中间下标的值和findVal进行比较;
2.1 如果arr(middle)>findVal,就应该向leftIndex<–(middle-1);
2.2 如果arr(middle)<findVal,就应该向(middle+1)—rightIndex;
2.3 如果arr(middle)=findVal,就找到;
2.4 上面2.1,2.2,2.3的逻辑会递归查找 - 分析退出递归的条件
if leftIndex>rightIndex{
//找不到…
return…
}
package main
import "fmt"
func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int) {
//判断leftIndex是否大于rightIndex
if leftIndex > rightIndex {
fmt.Println("找不到")
return
}
middle := (leftIndex + rightIndex) / 2
if (*arr)[middle] > findVal {
BinaryFind(arr, leftIndex, middle-1, findVal)
} else if (*arr)[middle] < findVal {
BinaryFind(arr, middle+1, rightIndex, findVal)
} else {
fmt.Printf("找到了,下标为%v\n", middle)
}
}
func main() {
arr := [6]int{1, 8, 10, 89, 1000, 1234}
BinaryFind(&arr, 0, len(arr)-1, 1000)
}
二维数组
入门案例
package main
import "fmt"
/*
0 0 0 0 0 0
0 0 1 0 0 0
0 2 0 3 0 0
0 0 0 0 0 0
*/
func main() {
//定义一个二维数组
var arr [4][6]int
//赋初值
arr[1][2] = 1
arr[2][1] = 2
arr[2][3] = 3
for i := 0; i < 4; i++ {
for j := 0; j < 6; j++ {
fmt.Print(arr[i][j], " ")
}
fmt.Println()
}
}
使用方法1:先声明/定义,再赋值
- 语法:var 数组名 [大小][大小]类型
- 比如 var arr [2][3]int,再赋值
- 二维数组在内存中的存在形式
使用方法2:直接初始化
- 声明:var 数组名 [大小][大小]类型 = [大小][大小]类型{{初值…},{初值…}}
- 赋值(有默认值)
- 基本使用
var arr [2][3]int = [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Println("arr=", arr)
- 说明:二维数组在声明/定义时也对应有四种写法
var 数组名 [大小][大小]类型 = [大小][大小]类型{{初值…},{初值…}}
var 数组名 [大小][大小]类型 = […][大小]类型{{初值…},{初值…}}
var 数组名 = [大小][大小]类型{{初值…},{初值…}}
var 数组名 = […][大小]类型{{初值…},{初值…}}
二维数组的遍历
- 双层for循环完成遍历
package main
import "fmt"
// 演示二维数组的遍历
func main() {
//双层for循环遍历
var arr = [2][3]int{{1, 2, 3}, {4, 5, 6}}
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Printf("%v\t", arr[i][j])
}
fmt.Println()
}
}
- for-range方式完成遍历
//for-range遍历二维数组
for i, v := range arr {
for j, v2 := range v {
fmt.Printf("arr[%v][%v]=%v\t", i, j, v2)
}
fmt.Println()
}
数组和查找相关案例
- 随机生成十个整数(1-100的范围)保存到数组,并且倒序打印以及求平均值、求最大值和最大值下标、并查找里面是否有55
package main
import (
"fmt"
"math/rand"
"time"
)
// 随机生成十个整数(1-100的范围)保存到数组,
// 并且倒序打印以及求平均值、求最大值和最大值下标、并查找里面是否有55
// 倒序输出
func daoxu(arr [10]int) {
temp := 0
for i := 0; i < len(arr)/2; i++ {
temp = arr[10-i-1]
arr[10-i-1] = arr[i]
arr[i] = temp
}
fmt.Println("倒序打印的数组为=", arr)
}
// 求平均值
func average(arr [10]int) {
sum := 0.0
for i := 0; i < len(arr); i++ {
sum += float64(arr[i])
}
fmt.Println("平均值为=", sum/float64(len(arr)))
}
// 求最大值
func max(arr [10]int) {
max := 0
index := 0
for i := 0; i < len(arr); i++ {
if arr[i] > max {
max = arr[i]
index = i
}
}
fmt.Printf("最大值是%d,最大值下标为%d\n", max, index)
}
// 判断数组中是否有55
func find(arr [10]int, findVal int) {
var num = -1
var index = 0
for i := 0; i < len(arr); i++ {
if arr[i] == findVal {
num = arr[i]
index = i
}
}
if num != -1 {
fmt.Printf("找到了,下标为%d", index)
} else {
fmt.Println("没有找到")
}
}
func main() {
var arr [10]int
rand.NewSource(time.Now().UnixNano())
for i := 0; i < 10; i++ {
arr[i] = rand.Intn(101)
}
fmt.Println("生成的数组为:", arr)
daoxu(arr)
average(arr)
max(arr)
find(arr, 55)
}
- 已知有个排序好(升序)的数组,要求插入一个元素,最后打印该数组,顺序依然是升序。
package main
import "fmt"
// 任意位置插入数字类型的元素
// @param slice []int 将指定元素插入的切片
// @param num int 插入的指定元素
// @param index int 插入的指定位置
func arbitrarilyInsertElement(slice []int, num int, index int) []int {
slice = append(slice[:index], append([]int{num}, slice[index:]...)...)
return slice
}
// 2、已知有个排序(升序)的数组,要求插入一个元素,最后打印该数组,顺序依然是升序
func exec2(arr [5]int, leftIndex int, rightIndex int, num int) bool {
slice := arr[:] // 将数组转成切片后才能新增元素
// 因为数组是升序的,所以可以使用二分查找来做exec2
middleIndex := (leftIndex + rightIndex) / 2 // 中间值的索引
middle := slice[middleIndex] // 中间值
if leftIndex > rightIndex {
// 防止右值索引小于0
if rightIndex < 0 {
rightIndex = 0
}
if num > arr[rightIndex] {
// num在中间值右侧,左值索引向右移动(+1),右值索引不变,所以使用rightIndex
// 因为左值索引向右移动了,所以当前左值索引(leftIndex)就是需要将num插入的位置
slice = arbitrarilyInsertElement(slice, num, leftIndex)
} else if num < arr[leftIndex] {
// num在中间值左侧,右值索引向左移动(-1),左值索引不变,所以使用leftIndex
// 因为右值索引向左移动了,所以当前右值索引(rightIndex)就是需要将num插入的位置
slice = arbitrarilyInsertElement(slice, num, rightIndex)
}
fmt.Println("slice=", slice)
return false
}
if num < middle {
rightIndex = middleIndex - 1
} else if num > middle {
leftIndex = middleIndex + 1
} else if num == middle {
// num等于中间值,所以可以直接使用中间值索引(middleIndex)作为将num插入的位置
slice = arbitrarilyInsertElement(slice, num, middleIndex)
fmt.Println("slice=", slice)
return true
}
return exec2(arr, leftIndex, rightIndex, num)
}
func main() {
arr := [5]int{1, 3, 5, 7, 9} // 升序数组
// 要插入的值
num := 0
fmt.Println("请输入插入的数字")
fmt.Scanln(&num)
exec2(arr, 0, len(arr)-1, num)
}
- 定义一个3行4列的二维数组,逐个从键盘输入值,编写程序将四角的数据清0
package main
import "fmt"
// 定义一个3行4列的二维数组,逐个从键盘输入值,编写程序将四周的数据清0
func clear(arr *[3][4]int) {
for i := 0; i < len(arr); i++ {
if i == 0 || i == len(arr)-1 {
for j := 0; j < len(arr[i]); j++ {
arr[i][j] = 0
}
} else {
for j := 0; j < len(arr[i]); j++ {
if j == 0 || j == len(arr[i])-1 {
arr[i][j] = 0
}
}
}
}
}
func main() {
var arr [3][4]int
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Printf("请输入第%v行,第%v个元素的值:\n", i+1, j+1)
fmt.Scanln(&arr[i][j])
}
}
clear(&arr)
for _, value := range arr {
for _, value2 := range value {
fmt.Print(value2, "")
}
fmt.Println()
}
}
- 定义一个4行4列的二维数组,逐个从键盘输入值,让后价格第一行和第四行的数据进行交换,第二行和第三行的数据进行交换。
package main
import "fmt"
// 定义一个3行4列的二维数组,逐个从键盘输入值,编写程序将四周的数据清0
func main() {
var multiArray = [4][4]int{}
for i := 0; i < len(multiArray); i++ {
for j := 0; j < len(multiArray[i]); j++ {
fmt.Printf("请输入第%v行,第%v个元素:\n", i+1, j+1)
fmt.Scanln(&multiArray[i][j])
}
}
for i := 0; i < len(multiArray); i++ {
for j := 0; j < len(multiArray[i]); j++ {
fmt.Print(multiArray[i][j], " ")
}
fmt.Println()
}
fmt.Println()
fmt.Println()
swap1 := [len(multiArray[0])]int{}
swap2 := [len(multiArray[1])]int{}
swap1 = multiArray[0]
multiArray[0] = multiArray[3]
multiArray[3] = swap1
swap2 = multiArray[1]
multiArray[1] = multiArray[2]
multiArray[2] = swap2
for i := 0; i < len(multiArray); i++ {
for j := 0; j < len(multiArray[i]); j++ {
fmt.Print(multiArray[i][j], " ")
}
fmt.Println()
}
}
- 试保存1 3 5 7 9 五个奇数到数组,并倒序打印
package main
import "fmt"
// 定义一个3行4列的二维数组,逐个从键盘输入值,编写程序将四周的数据清0
func main() {
var arr = [5]int{}
for i := 0; i < len(arr); i++ {
fmt.Printf("请输入第%v个奇数\n", i+1)
fmt.Scanln(&arr[i])
}
fmt.Println(arr)
tempIndex := len(arr)
tempArr := [5]int{}
for i := 0; i < len(arr); i++ {
tempIndex--
tempArr[tempIndex] = arr[i]
}
arr = tempArr
fmt.Println(arr)
}
- 试写出实现查找的核心代码,比如已知数组arr[10]string:里面保存了十个元素,先要查找“AA”在其中是否存在,打印提示,如果有多个“AA”,也要找到对一个的下标。
package main
import "fmt"
//试写出实现查找的核心代码,比如已知数组arr[10]string:
//里面保存了十个元素,先要查找“AA”在其中是否存在,
// 打印提示,如果有多个“AA”,也要找到对一个的下标。
func search(sliceArr []string, target string) (targetIndex []int) {
count := 0
for i := 0; i < len(sliceArr); i++ {
if sliceArr[i] == target {
targetIndex = append(targetIndex, i)
count++
}
}
if count == 0 {
fmt.Printf("数组中没有'%s'\n", target)
} else {
fmt.Printf("'%s'在数组中有%d个\n", target, count)
}
return targetIndex
}
func main() {
var arr = []string{"上海", "杭州", "北京", "深圳", "杭州"}
var target string
fmt.Println("请输入想要查找的城市:")
fmt.Scanln(&target)
fmt.Printf("该元素对应的下标为:%v\n", search(arr, target))
}
- 随机生成10个整数(1-100)之间,使用冒泡排序法进行排序,然后用二分查找发,查找是否有90这个数,并显示下标,如果没有则提示“找不到该数”。
package main
import (
"fmt"
"math/rand"
"time"
)
// 随机生成10个整数(1-100),使用冒泡排序进行排序,然后用二分查找法查找是否有90这个数,
// 并显示下标,如果没有则提示“找不到该数”。
func bubbleSort(arr []int) []int {
temp := 0
for i := 0; i < len(arr)-1; i++ {
for j := 0; j < len(arr)-1-i; j++ {
if arr[j] > arr[j+1] {
temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
return arr
}
func finary(arr []int, leftIndex int, rightIndex int, target int) {
midIndex := (leftIndex + rightIndex) / 2
if rightIndex < leftIndex {
fmt.Println("找不到该数")
return
}
if target > arr[midIndex] {
finary(arr, midIndex+1, rightIndex, target)
} else if target < arr[midIndex] {
finary(arr, midIndex+1, rightIndex, target)
} else if target < arr[midIndex] {
finary(arr, leftIndex, midIndex-1, target)
} else {
fmt.Println("该数存在,下标为", midIndex)
}
}
func main() {
var arr = make([]int, 10)
rand.NewSource(time.Now().UnixNano())
for i := 0; i < 10; i++ {
arr[i] = rand.Intn(101)
}
fmt.Println(arr)
arr = bubbleSort(arr)
fmt.Println(arr)
finary(arr, 0, len(arr)-1, 90)
}
- 编写一个函数,可以接收一个数组,该数组有5个数,请找出最大的数和最小的数和对应的数组下标是多少.
package main
import "fmt"
// 编写一个函数,可以接收一个数组,该数组有5个数,
// 请找出最大的数和最小的数和对应的数组下标是多少
func findNum(arr *[5]int) (max int, maxIndex int, min int, minIndex int) {
max = (*arr)[0]
min = (*arr)[0]
for i := 0; i < len(arr); i++ {
if (*arr)[i] > max {
max = (*arr)[i]
maxIndex = i
}
}
for i := 0; i < len(arr); i++ {
if (*arr)[i] < min {
min = (*arr)[i]
minIndex = i
}
}
return max, maxIndex, min, minIndex
}
func main() {
var arr [5]int = [5]int{12, 34, 45, 23, 17}
max, maxIndex, min, minIndex := findNum(&arr)
fmt.Printf("max = %v ,maxIndex = %v ,min = %v ,minIndex = %v ", max, maxIndex, min, minIndex)
}
- 定义一个数组,并给出8个整数,求该数组中大于平均值的数的个数,和小于平均值的数的个数
package main
import "fmt"
// 定义一个数组,并给出8个整数,求该数组中大于平均值的数的个数,和小于平均值的数的个数
func function(arr *[8]int) (num1 int, num2 int) {
var sum float64
for i := 0; i < len(*arr); i++ {
sum += float64((*arr)[i])
}
average := sum / float64(len(*arr))
for i := 0; i < len(*arr); i++ {
if float64((*arr)[i]) > average {
num1++
} else {
num2++
}
}
return num1, num2
}
func main() {
arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
num1, num2 := function(&arr)
fmt.Printf("大于平均数的个数是%v,小于平均数的个数是%v", num1, num2)
}
- 跳水比赛:8个评委打分。运动员的成绩是8个成绩去掉一个最高分,去掉一个最低分,剩下的六个分数的平均分就是最后得分。使用一维数组实现如下功能:
(1)请把打最高分的评委和最低分的评委找出来
(2)找出最佳评委和最差评委。最佳评委就是打分和最后得分最近的评委。最差评委就是打分和最后得分相差最大的
package main
import "fmt"
/*
跳水比赛,8个评委打分,运动员的成绩是8个成绩中去掉一个最高分,去掉一个最低分,
剩下6个成绩的平均分就是最终得分。使用一维数组实现以下功能:
1.把打最高分的评委和打最低分的评委找出来
2.找出最佳评委和最差评委。
最佳评委就是打分和最终成绩最接近的评委,最差评委就是打分和最终成绩相差最大的评委
*/
//冒泡排序
func scoreBubble(score []float32) (bubble []float32) {
var temp float32
for k := 0; k < len(score)-1; k++ {
for i := 0; i < len(score)-1-k; i++ {
if score[i] > score[i+1] {
temp = score[i]
score[i] = score[i+1]
score[i+1] = temp
}
}
}
return score
}
// 求最终成绩的函数
func finalScore(bubbleScore []float32) float32 {
finalScoreList := bubbleScore[1 : len(bubbleScore)-1]
var sum float32
for i := 0; i < len(finalScoreList); i++ {
sum += finalScoreList[i]
}
return sum / float32(len(finalScoreList))
}
// 求出最差评委所打分数的函数
// 最差评委:用最终得分和最高成绩(即数组最末尾的元素)及最低成绩(即数组第一个元素)相减
// 如果最低成绩和最终成绩的差大于最高成绩和最终成绩的差,那么最差评委就是打最低成绩的那个评委
func worstJudge(finalScore float32, bubbleScore []float32) float32 {
if (finalScore - bubbleScore[0]) > (bubbleScore[len(bubbleScore)-1] - finalScore) {
return bubbleScore[0]
} else {
return bubbleScore[len(bubbleScore)-1]
}
}
// 求出最佳评委所打分数的函数
// 最佳评委:用最终成绩和每一个评委打出的成绩求差,差最小的为最佳评委
// 用最终成绩左右两边最接近的成绩分别与最终成绩求差,差最小的为最佳评委
func bestJudge(finalScore float32, bubbleScore []float32) float32 {
var finalLeft float32
var finalRight float32
for i := 0; i < len(bubbleScore); i++ {
if finalScore > bubbleScore[i] {
finalLeft = bubbleScore[i]
finalRight = bubbleScore[i+1]
}
}
if (finalScore - finalLeft) < (finalRight - finalScore) {
return finalLeft
} else {
return finalLeft
}
}
func main() {
var rowScore = [8]float32{5, 6.5, 9, 5, 9.5, 7, 5.5, 9.5} //将原始成绩单独保存
//新建一个数组用来存放原始成绩,目的是防止排序操作后原始成绩也跟着排序,
//如果原始成绩也跟着排序,那么就无法比对出最佳和最差成绩所在的位置了
var score = [8]float32{}
score = rowScore //将原始成绩赋给新建数组
bubbleScore := score[:] //对其切片处理,目的是传入函数
bubbleScore = scoreBubble(bubbleScore) //调用将成绩排序的函数,并接收返回值
finalScore := finalScore(bubbleScore) //调用求最终成绩的函数,并接收返回值
fmt.Printf("最终成绩为:%.1f\n", finalScore)
worstJudge := worstJudge(finalScore, bubbleScore) //调用求最差成绩的函数,并接收返回值
bestJudge := bestJudge(finalScore, bubbleScore) //调用求最佳成绩的函数,并接收返回值
//最后将最差成绩和最佳成绩放到原始成绩里比对,找出其所在位置
fmt.Println("原始成绩:", rowScore)
for i := 0; i < len(rowScore); i++ {
if rowScore[i] == worstJudge {
fmt.Printf("最差评委是打%.1f分的评委,该分数被第%d个评委打出\n", worstJudge, i)
}
}
for i := 0; i < len(rowScore); i++ {
if rowScore[i] == bestJudge {
fmt.Printf("最佳评委是打%.1f分的评委,该分数被第%d个评委打出\n", bestJudge, i)
}
}
}