玩转算法与数据结构 C++描述 选择排序

慕课网 玩转算法与数据结构 笔记

1-1 我们究竟为什么学习算法

 

计算机专业的同学对算法不陌生,

为什么要学习算法?

 

很多同学是为了应付面试,大公司都需要

但是我们应该体会到,算法是至关重要的

 

很多人认为自己工作里,和算法都没有关系

但是编译器里都是算法帮助我们完成的,封装在工具中,我们意识不到他的存在,认为是理所当然的,

 

搜索引擎:搜索算法+排序算法

1亿个结果用0.67s 排序出有用信息

 

算法的另一种用途:智能

Siri能够理解人类的语言

 

推荐算法:amazon,ebay,TouTube

推荐喜欢的东西:机器学习

机器学习的基础也是基本算法的使用

 

前面的例子都是和数据打交道,

现在的动画制作,也不是一帧一帧画出来的,每个画面都包括了对算法的研究:毛发,运动,灯光,天空,水,全部是算法 - 图形学算法

 

自动生成迷宫的算法,制作扫雷的算法:flatfill

算法处理三消游戏

 

AlphaGo:深度学习

 

计算机视觉:根据图像作为输入来识别出信息

人脸识别,

 

AR技术,

 

photoshop抠图

 

压缩软件

 

数据库本身就是算法库,他封装的很好,我们使用sql语句就可以使用

 

学好算法,才能创造出更有意义的东西,

而不是简单的把数据取出来用就完事了

 

公认的算法的学习曲线非常陡,

算法像数学,无法很快得到成果

要从最基础的开始积累,一点一点的前进

 

 

1-2 课程介绍

 

迫不及待要学习算法了,

语言用C++,因为大家本科都学过C++

算法其实和语言是无关的,算法导论都是伪代码,算法其实是一种思想,想通了,写出来很简单,

 

根据不同的语言特性,不同的语言有各自巧妙之处

不同语言的算法在:

https://github.com/liuyubobobo/Play-with-Algorithms

--

课程代码更新也在这个github上,

需要掌握基础的语言知识,对数组,链表,堆,栈等线性结构有了解

一般同学对以上这些基础都掌握的很好,所以这里不做介绍

对算法基础知识有了解:递归,遍历,算法复杂度

 

学习路径:

线性(排序),他是算法领域非常重要的,通过学习可以了解很多思想

树形结构,应用场合,每种树的特点,局限性

 

对算法的学习,编程是次要的,而需要多写写画画,才能更好的学习算法,

想明白了,编码并不难

很多复杂的算法,十几行就可以搞定

 

白板编程:

不给编译器,直接写代码,要求它可以被直接编译运行

这种编程不可能很长,一般就是10几行

 

数据结构的重要性:

linus说,好坏programmer的区别,就是他顾虑code还是datastucture,bad programmer worry about code,good programmer worry adbout data structure and their relationships,

 

Algorithm + Data Structures = Programs

 

很多大企业的面试题,听起来像算法,本质是数据结构题,

一般不难,考察的都是基础

微软要求白板写堆,能不能写出二叉树反转这么简单的算法

 

基础打牢,才会有思路,一味追求高端精巧,就是看到一个问题,学到一个想法,而不是系统的把它们整理在一个框架里面。

 

算法思想?

分支算法 - 归并排序,快速排序

贪心算法 - 最小生成树

动态规划 - 最短路径

递归搜索 - 树形结构

 

面试问题

会在评论区和github提出一些题目

 

每个细分领域都是算法

不积跬步无以至千里

图形学,图像学,机器学习,人工智能,数据挖掘,

操作系统,编译原理,网络安全,虚拟现实,高性能计算

 

很多学习对人工智能感兴趣,但是学一会儿就学不动了,究其根本,是基础太差

 

要耐心,打牢基础

 

A programmer who subconsiciously views himself as an artise will enjoy wht he does and will do it better.

 

 

2-1 选择排序法 Selection Sort

先接触O(n^2)的排序算法,

对于排序算法,最优的应该是nlogn

 

那为什么还要学习O(n^2)级别的排序算法?

1,因为很基础,不能小瞧其思路,简单的方法可以加深对问题的理解,增加启发,然后优化

如果对一些面试问题没思路,可以先写出简单的解法,过程中可能就有了优化的思路

,2,编码简单,易于实现,是一些简单场景的首选,

3,特殊情况,简单的排序算法更加有效

4,简单的排序算法可以衍生出复杂的排序算法。如希尔排序,它是对插入排序进行优化,得到的

5,简单的排序,可以作为子过程,改进更加复杂的排序算法

 

Selection Sort 选择排序

8 6 2 3 1 5 7 4

 

有一段动画演示,就是每次都找出所有剩余元素里,最小元素,拿出来,这样每次排出来的都是对的结果。

 

 

写代码

#include <iostream>
#include <algorithm> //老版本swap()在algorithm
using namepsace std; //C++ 11的swap()在std

void selectionSort(int arr[], int n){
    for(int i=0; i<n; i++){
        //寻找[i,n)前闭后开这个区间里面的最小值
        int minIndex = i;
        
        for(int j=i+1; j<n; j++){
            if(arr[j]<arr[minIndex])
                minIndex = j;
        }//这段就找到了[i,n)区间的最小值,最小值的索引放在了minIndex中
        swap(arr[i], arr[minIndex]);
    }
}

int main(){
    //test
    int a[10] = {10,9,8,7,6,5,4,3,2,1};
    selsetionSort(1, 10);
    for(int 1=0; i<10; i++)
        count<<a[i]<<" ";
    cout<<endl;    
    
    return 0;
}

 

2-2 使用模板(泛型)编写算法

现在只能对整数排序,

但是实际可能要对浮点数,字符串,自己定义的结构体和类排序,

所以应该使用模板函数,有些语言叫做泛型

 

再试试使用自定义的类型进行排序

#include<iostream>
#include<algorithm>
#include <String>
using namespace std;
#include "student.h"


timplate<typename T> //定义类型为T,然后后面都用类型T
void selectionSort(T arr[], int n){
    for(int i = 0; i < n; i ++){
        //寻找[i,n)里面的最小值
        int minIndex = i;
        for(int j=0; j<i; j++){
            if(arr[j] > arr[minIndex]){
                minIndex = j;
            }
        }
        //把最小值的索引放入minIndex,
        swap(arr[i], arr[minIndex]); 
    }
}

int main(){
    String c[4] = {"D", "C", "B", "A"};
    slesctionSort(c, 4);
    for(int i = 0; i < 4: i++){
        cout<<b[c]<<" ";
    }
    cout<<endl;
    
    //用{}初始化结构体
    Student d[4] = {{"d",90}, {"c",100}, {"b",95}, {"A",95}};
    selectionSort(d,4);
    for(int i=0; i<4; i++){
        count<<d[i];
    }
    
    
    return 0;
}

//Student.h 
#ifndef SELECTIONSORT_STUDEENT_H
#define SELECTIONSORT_STUDENT_H

#include <iostream>
#include <string>

//很多C++从业者认为不该引入std,污染命名空间
using namespace std;

//struct也可以重载?
struct Student{
    string name;
    int score;
    
    //重载<, 传入另外一个学生的引用,返回bool
    bool operator<(const Student &otherStudent){
        //return score > otherStudent.score;
        //这样会把name也排序,
        return score != otherStudent.score?
            score>otherStudent.score : name < otherStudent.name;
    }
    
    //重载<<,打印用
    //为什么用friend?
    friend ostream& operator<<(ostream &os, const Student &student){
        os<<"Student: "<<student.name<<" "<<student.score<<endl;
        return os;
    }
}

#endif 

 

更好的方法是把元素的比较写成函数less(),根据具体情况定义什么叫 一个比另一个小

这样会更加灵活

 

 

2-3 随机生成算法测试用例

原来的测试用例非常不智能,测试数组是硬编码的,

后面可能用到10万个元素,就应该用程序成员

 

创建新的SortTestHelper.h

//SortTestHelper.h
#ifndef SELECTIONSORT_SORTTESTHELPER_H
#define SELECTIONSORT_SORTTESTHELPER_H

#include <iostream>
#include<ctime> //老版本需要
#include<cassert>
using namespace std;

//辅助测试的函数放在一个命名空间里面
//这样方便后面调用时了解这些函数都是辅助测试的
namespace SortTestHelper{
    //生成有n个元素的随机数组,每个元素的随机范围是[rangeL, rangeR]
    //实现就直接写到.h了,这样方便,可以理解为开源工程的项目
    //而且SortTestHelper还有很多要用到模板的函数,他们的实现必须在.h,为统一,就这么做了
    int* generateRandomArray(int n, int rangeL, int rangeR){
        //L应该小于R
        assert(rangeL <= rangeR);
        
        int &arr = new int[n];
        //c/c++标准的随机种子,根据时间
        srand(time(NULL));
        for(int i=0; i<n; i++){
            //rand返回随机整数,后面给出范围,%它
            //前部分只是生成0~(R-L)的整数,后面再加一个L偏移
            arr[i]=rand() % (rangeR - rangeL + 1) + rangeL;
        }
        return arr;
        
    }
    
    template<typename T>
    void printArray(T arr[], int n){
        for(int i = 0; i < n; i++)
            count << arr[i] << " ";
        count << endl;
        return;
    }
    
    //测试排序结果的正确性
    template<Typename T>
    bool isSorted(T arr[], int n){
        for(int i=0; i<n-1; i++){
            if(arr[i] > arr[i+1])
                return false;
        }
        return true;
    }
    
    template<Typename T>
    void testSort(string sortName, void(*sort)(T[], int), T arr[], int n){
        //要测试时间,时间差就是消耗的时间,老版本也需要<ctime>
        clock_t startTime = clock();
        //回调
        sort(arr, n);
        clock_t endTime = clock();
        
        assert( isSorted(arr, n));
        
        //打印执行了多少秒
        cout << sortName << ":" << double(endTime - startTime) / CLOCKS_PER_SEC << "s" <<endls;
    }

}

#endif

//main.cpp
int main(){
    int n = 10000;
    int *arr = SortTestHelper::generateRandomArray(n, 0, n);
    selectionSort(arr, n);
    SortTtestHelper::printArray(arr, n);
    
    delete[] arr;
    
    return 0;
}

===========

2-4 测试算法的性能

写一个很有趣,很重要,辅助研究算法的函数

要对算法的性能有感性的认识,看算法在特定的数据集上的执行时间


 

int main(){
    SortTestHelper::testSort("Selection Sort", selectionSort, arr, n);
}

//10000数据用0.2s,10万用了18s,大概是100倍。所以时间复杂度是n^2级别的,就是数据量和时间的关系

 

这两节的准备,就是为了后面学习排序算法,后面还会介绍一个o n^2级别的排序:插入排序

通过下节的学习,你会明白,o n^2级别的排序算法也很有用

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值