1. 冒泡排序
数组中每一个元素与其后一个元素进行比较,较大的或者较小的放在后面,即其与后面这个元素换位置。即第一个与第二个比,第二个与第三个比,知道倒数第二个与倒数第一个比,之后在数组的末尾就是最大或者最小的数了;之后继续上一个步骤,不过由于上一趟排序中,最后一个数已经为有序的,所以只需要比较到倒数第三个与倒数第二个比较;之后比较的次数逐次减少。
java代码
private void bubbling(int[] arr){
for (int i = 0; i < arr.length - 1; i++){
boolean hasChange = false;
for (int j = 0; j < arr.length - i - 1; j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
hasChange = true;
}
}
if (!hasChange){
break;
}
}
}
效率问题:
慢…
进行80000个数字的排序,效率让人心碎:
public static void main(String[] args) {
int[] arr = new int[80000];
Random random = new Random();
for (int i = 0; i < arr.length; i++){
arr[i] = random.nextInt(800000);
}
SortDemo sortDemo = new SortDemo();
System.out.println(System.currentTimeMillis());
sortDemo.bubbling(arr);
System.out.println(System.currentTimeMillis());
}
输出如下:
1585325682398
1585325701677
19秒左右0.0
Go实现
func Bubbling(arr []int){
for i := 0; i < len(arr) - 1; i++ {
count := false
for j := 0; j < len(arr) - i - 1; j++ {
if (arr[j] > arr[j + 1]) {
temp := 0
temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
count = true
}
}
if(!count){
break;
}
}
}
效率问题:
也是一个慢字…毕竟冒泡 伤不起
80000个数字进行排序
func main(){
arr := make([]int, 80000)
t := time.Now()
fomat := t.UnixNano()
rand.Seed(fomat)
for i := 0; i < len(arr); i++{
arr[i] = rand.Intn(80000)
}
t1 := time.Now()
Bubbling(arr)
t2 := time.Since(t1)
fmt.Println(t2)
}
输出如下:
15.9531755s
2. 选择排序
思路:从数组中选出一个最大(最小)值,放入数组头,或者数组尾,区别在于正向反向,全看自己心情,此处使用正向升序排序。每次找出最小值放入无序端的第一个,即第i个,i从0开始
废话不多说了,就是选出最小的往前放,上代码:
java实现
private void choice(int[] arr){
for (int i = 0; i < arr.length - 1; i++){
int min = arr[i];
int minIndex = i;
for (int j = i + 1; j < arr.length; j++){
if (arr[j] < min){
min = arr[j];
minIndex = j;
}
}
if (i != minIndex){
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
效率问题:比起冒泡是好了不少,但时间复杂度还是O(n^2)
依然是那熟悉的80000比数据
public static void main(String[] args) {
int[] arr = new int[80000];
Random random = new Random();
for (int i = 0; i < arr.length; i++){
arr[i] = random.nextInt(800000);
}
SortDemo sortDemo = new SortDemo();
System.out.println(System.currentTimeMillis());
sortDemo.choice(arr);
System.out.println(System.currentTimeMillis());
}
输出结果:
1585326261907
1585326268503
7秒左右,确实比老爷爷冒泡跑的快了点
Go实现
func choice(arr []int){
for i := 0; i < len(arr) - 1; i++{
min := arr[i]
minIndex := i
for j := i + 1; j < len(arr); j++{
if (arr[j] < min){
min = arr[j]
minIndex = i
}
}
if (i != minIndex){
temp := arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
}
}
再以80000比数据跑一遍:
func main(){
arr := make([]int, 80000)
t := time.Now()
fomat := t.UnixNano()
rand.Seed(fomat)
for i := 0; i < len(arr); i++{
arr[i] = rand.Intn(80000)
}
t1 := time.Now()
choice(arr)
t2 := time.Since(t1)
fmt.Println(t2)
}
输出如下:
6.6854358s
和java兄弟并排走,时间差不多
3. 插入排序
插入排序的思想是,将数组分为两部分,左边为有序部分,右边为无序部分。每次从无序部分取出第一个,与有序部分分别比较,并将其插入到有序部分中,至此,有序部分加一,无序部分减一。当无序部分长度减为0时,整个数组都为有序了
如何插入?
如何理解插入这个命名呢,我们定义insertIndex
为待被插入的下标,如果我们当前采用的比较的数比arr[insertIndex]
小(假设是升序排列),那么将arr[insertIndex]
向后放,即有如下数组:
[3, 1, 2]
我们选1作为insertValue
,insertIndex
为0,那么此时比较发现 arr[insertIndex] > insertValue
,那么我们把3
提起来往后放,放到arr[insertIndex + 1]
的位置,之后再往前比,发现已经到了最前,就可以将arr[insertIndex] = insertValue
;只是举了一个简单的例子,如果是:
[2, 3, 4, 5, 1]
我们此时遍历到最后一个元素,即1,此时insertValue = 1
,insertIndex = 3
,那么如下就是该数组的更改顺序:
1. [2, 3, 4, 5, 5] insertValue = 1 insertIndex = 3
2. [2, 3, 4, 4, 5] insertValue = 1 insertIndex = 2
3. [2, 3, 3, 4, 5] insertValue = 1 insertIndex = 1
4. [2, 2, 3, 4, 5] insertValue = 1 insertIndex = 0
5. [1, 2, 3, 4, 5] 最后当 insertIndex = 0 时,给 arr[insertIndex] = insertValue
具体分析
int insertValue = arr[1] //需要插入进去的值
Int insertIndex = 1 -1 //将要被插入的元素的前一个元素的下标
while(insertIndex >= 0 && insertValue < arr[insertIndex]){
//上述判断: 当元素被插入进有序部分后,如果他前面的待被插入元素仍大于等于0, 说明它前面仍然有元素
//当被插入元素的值小于它前面待被插入的元素,说明它现在没有和目前的前一个元素比较过,
//仍然没有确定位置,所以需要再进行比较,当它前面没有元素或者它前面的元素的值小于它,说明它已经正确插入了有序部分
//只要进入循环,说明它已经执行了一次插入动作,插入的实现为将待插入的元素与前面的元素换位
arr[insertIndex + 1] = arr[insertIndex]
//当换位后,目前待被插入的元素就是目前其所在位置的前一个了,
//因为该元素向前插入了一个位置,所以待被插入的元素位置也向前移动
insertIndex --
}
//因为上述循环中,如果在最后一次循环,插入元素的前面并没有元素了,或者前一个元素比它小,但是
//insertIndex仍然减一了 所以,待插入的元素的正确位置应该是insertIndex+1
arr[insertIndex + 1] = insertValue
在插入排序中,我们在刚开始时总认为第一个元素自己组成了有序部分,因为也没有其他元素和他比较大小。所以无序部分的第一个起始为1,之后每次向后加,我们只需要给这个步骤加一个循环即可;
java实现
private void insertSort(int[] arr){
//循环表示第几个被插入的数 从1开始
for (int i = 1; i < arr.length; i++){
//需要插入的数为
int insertValue = arr[i];
//待被插入的下标 在待插入数的前一个
int insertIndex = i - 1;
//insertIndex >= 0 当insertIndex小于0时,说明待插入数之前已经没有数据了,它是最小的数
//insertValue < arr[insertIndex] 当待插入的数小于前一个数时,还不知道它是不是最小,它还需要与它前前一个进行
//比较,但是在此时他已经可以插队到他前一个数之前
while (insertIndex >= 0 && insertValue < arr[insertIndex]){
// 将当前的位置的数向后移动一位
arr[insertIndex + 1] = arr[insertIndex];
// 将下标值减一,表示将要与当前位置的前一个位置比较
insertIndex--;
}
arr[insertIndex + 1] = insertValue;
}
}
效率分析:
public static void main(String[] args) {
int[] arr = new int[80000];
Random random = new Random();
for (int i = 0; i < arr.length; i++){
arr[i] = random.nextInt(800000);
}
SortDemo sortDemo = new SortDemo();
System.out.println(System.currentTimeMillis());
sortDemo.insertSort(arr);
System.out.println(System.currentTimeMillis());
}
1585326807369
1585326809251
比起选择排序更快
Go实现
func insertSort(arr []int){
for i := 1; i < len(arr); i++{
insertValue := arr[i]
insertIndex := i - 1
for {
if (insertIndex < 0 || insertValue > arr[insertIndex]){
break
}
arr[insertIndex + 1] = arr[insertIndex]
insertIndex--
}
arr[insertIndex + 1] = insertValue
}
}
执行效率:
func main(){
arr := make([]int, 80000)
t := time.Now()
fomat := t.UnixNano()
rand.Seed(fomat)
for i := 0; i < len(arr); i++{
arr[i] = rand.Intn(80000)
}
t1 := time.Now()
insertSort(arr)
t2 := time.Since(t1)
fmt.Println(t2)
}
执行结果:
1.8607944s
如果走的不快,那就多走几步。