《算法笔记》之算法初步

本篇主要内容:

基础算法:排序、散列、递归、贪心、二分以及其他高效技巧

一些算法题:八皇后、汉诺塔等

1.排序:

排序主要可以分为:选择排序、插入排序

选择排序:

  主要介绍最为常用的简单选择排序

  简单选择排序:基本思路是将一个序列分为1~n个单元,首先从这n个单元里选择出来最小的单元,将其与第一个位置的单元相交换,接着从2~n个单元里,找出最小单元,将其与第二个位置的单元相交换,依次进行下去......。这样在n趟操作之后就会形成一个从小到大的有序序列。

 

代码实现:一共进行n次操作,嵌套for循环,每次找出从i到n之中最小的数,然后与第i个位置数据进行交换

void SelectSort( int a[], int length )
{
    //对数组a排序,length是数组元素数量
    for( int i = 0; i < length; i++ ) {
        // 找到从i开始到最后一个元素中最小的元素,k存储最小元素的下标.
        int k = i;
        for( int j = i + 1; j < length; j++ ) {
            if( a[j] < a[k] ) { k = j; }
        }

        // 将最小的元素a[k] 和 开始的元素a[i] 交换数据.
        if( k != i ) {
            int temp;
            temp= a[k];
            a[k] = a[i];
            a[i] = temp;
        }
    }
}

插入排序:

  主要介绍直接插入排序

  直接插入排序:从2~n进行n-1次操作。假设这个时候是第i+1个数据,且前面1~i个数据均为有序数据,则将第i个数据插入到前面数据之中,因此可以采用for循环从2~n,内部嵌套上while循环,进行查找1~i之中第i+1个数据适合放在哪里。

void SelectSort(){
    for(int i=1;i<n;i++){
        int temp = A[i], j=i;
        while(j>0 && temp<A[j-1]){
            A[j] = A[j-1];
            j--;
        }
        A[j] = temp;
    }
} 

实例:

#include <iostream>
using namespace std;
int main(){

    int num[5] = {3, 7, 1, 8, 5};
    int i,j;
    int length = 5;

    for (i = 1; i < length; i++)
    {
        int temp = num[i];    
        j = i;    
        while ( j > 0 && num[j-1] > temp)
        {
            num[j] = num[j-1];
            j--;
        }
        num[j] = temp;    
    }
    
    for(int k=0;k<length;k++){//打印数组 
        cout<<num[k];
    }
    return 0;
}
结果:13578

选择排序之实例剖析:

#include<iostream>
using namespace std;

const int MAX_NUM = 100; 
//选择排序 
int main(){
    int a[MAX_NUM];
    int n;
    cin >> n;                       //共有n个整数待排序 
    for(int i = 0; i < n; ++i)      //输入n个整数 
        cin >> a[i];
    //下面对整个数组进行从小到大排序
    for(int i = 0; i<n-1 ;++i){     //每次循环将第i小的元素放好 
        int tmpMin = i;  //用来记录从第i个到第n-1个元素中,最小的那个元素的下标 
        for(int j=i; j<n;++j){
            if(a[j]<a[tmpMin])
                tmpMin = j;
        }
    //下面将第i小的元素放在第i个位置上,并将原来占着第i个位置的那个元素挪到后面
        int tmp = a[i];
        a[i] = a[tmpMin];
        a[tmpMin] = tmp; 
    }
    //下面两行将排序好的n个元素输出
    for(int i=0;i<n;++i)
        cout << a[i] << endl;
    return 0;
}

插入排序之实例剖析:

#include<iostream>
using namespace std;

//插入排序
void InsertionSort(int a[], int size)
{
    int i;          //有序区间的最后一个元素的位置,i+1就是无序区间最左边元素的位置
    for(i = 0; i < size-1; ++i){
        int tmp = a[i + 1];  //tmp是待插入到有序区间的元素,即无序区间最左边的元素
        int j = i;
        while(j >= 0 && tmp < a[j]){        //寻找插入的位置 
            a[j + 1] = a[j];                //比tmp大的元素都往后移动 
            --j;
        }
        a[j + 1] = tmp;
    }
}

//输出二维数组,rows是行数
void PrintArray(int a[][5], int rows)
{
    for(int i = 0; i < rows; ++i){
        for(int j = 0; j < 5; ++j)
            cout << a[i][j] << " ";
        cout << endl;
    }
} 


//主函数 
int main(){
    int b[5] = {50, 30, 20, 10, 40};
    int a2d[3][5] = {{5, 3, 2, 1, 4},{10, 20, 50, 40, 30},{100, 120, 50, 140, 30}};
    InsertionSort(b, 5);
    for(int i = 0; i < 5; ++i)
        cout << b[i] << " ";
    cout << endl;
    for(int i = 0; i < 3; ++i)      //将 a2d每一行均排序 
        InsertionSort(a2d[i], 5);
    PrintArray(a2d, 3);
    return 0;
}

 

2.散列

  哈希表(也可以叫做散列表),是根据  键(Key)而直接访问在内存存储位置的数据结构,也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做哈希表。

  哈希函数构造:

  如果字符串S由A~Z组成:

  

 

   如果字符串S由A~Z/a~z组成:

  

 

 3.全排列问题(递归):

//全排列:视频参考哔哩哔哩:正月点灯笼 
#include <stdio.h>

void swap(int A[], int i, int j){//用于交换数组的2个值 
    int temp = A[i];
    A[i] = A[j];
    A[j] = temp;
}

void printArray(int A[], int n){//用于打印数组 
    int i;
    for(i=0;i<n;i++){
        printf("%d ", A[i]);
    }
    printf("\n");
}

void perm(int A[], int p, int q){//全排列递归函数;p,q代表对数组A全排列的起始和结束 
    if(p == q){//递归结束条件 
        printArray(A, q+1);
    }
    else{
        int i;
        for(i=p; i<=q; i++){
            swap(A, p, i);
            perm(A, p+1, q);
            swap(A, p, i);
        }
    }    
} 

int main(){
    int A[4] = {1, 2, 3, 4};
    perm(A, 0, 3);
    return 0;
}

4.N皇后问题:

//参考视频:小甲鱼算法视频(八皇后问题) 
#include <stdio.h>

//count代表:共有多少种解决方法 
int count = 0; 
//row:表示起始行
//n:表示列数 
//(*chess)[8]:指向棋盘的行指针 

int noDanger(int row, int j, int (*chess)[8]){//判断可不可以放棋子:为1,可以;为0,不可以; 
    int i, k, flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0, flag5 = 0;
    //判断列的方向 
    for(i=0; i<8; i++){
        if(*(*(chess+i)+j)!=0){
            flag1 = 1;
            break;
        }
    }
    //判断左上方
    for( i=row, k=j; i>=0 && k>=0; i--, k--){
        if(*(*(chess+i)+k)!=0){
            flag2 = 1;
            break;
        }
    }
    //判断右下方 
    for( i=row, k=j; i<8 && k<8; i++, k++){
        if(*(*(chess+i)+k)!=0){
            flag3 = 1;
            break;
        }
    }
    //判断右上方 
    for( i=row, k=j; i>=0 && k<8; i--, k++){
        if(*(*(chess+i)+k)!=0){
            flag4 = 1;
            break;
        }
    }
    //判断左下方 
    for( i=row, k=j; i<8 && k>=0; i++, k--){
        if(*(*(chess+i)+k)!=0){
            flag5 = 1;
            break;
        }
    }
    if(flag1 || flag2 || flag3 || flag4 || flag5){
        return 0;//0:代表不可以摆放棋子 
    }else{
        return 1;//1:代表该摆放位置正确,可以摆放棋子 
    }
}

//EightQueen:主要的递归函数 
//(*chess)[8]:指向棋盘的行指针
//row指从哪一行开始
//n代表总列数 
void EightQueen(int row, int n, int (*chess)[8]){
    int chess2[8][8], i, j;
    for(i=0; i<8; i++){//复制chess数组 
        for(j=0; j<8; j++){
            chess2[i][j] = chess[i][j];
        }
    }
    if(row == 8){//结束条件:当到达最后一行的时候 
        printf("第%d种\n", count+1); 
         for(i=0; i<8; i++){//打印出来解决方法 
            for(j=0; j<8; j++){
            printf("%d ", *(*(chess2+i)+j));
        }
        printf("\n");
      }
      printf("\n");
      count++; //解决方法+1 
    }
    else{//递归体 
        for( j=0; j<n; j++){
            if(noDanger(row, j, chess)){//noDanger:为1代表可以摆放棋子;为0代表不可以摆放棋子 
                for( i=0; i<8; i++){//首先把这一行所有棋子先全置为0 
                    *(*(chess2+row)+i) = 0;
                }
                *(*(chess2+row)+j) = 1;//将noDanger为1的第row行第j列置为1 
                EightQueen(row+1, n, chess2);//递归进行下一行的探测 
            }
        } 
    } 
}

int main(){
    int chess[8][8], i, j;
    for(i=0; i<8; i++){
        for(j=0; j<8; j++){
            chess[i][j] = 0;
        }
    }
    EightQueen(0, 8, chess);
    printf("\n一共有%d种解决方法", count);
    return 0;
} 

5.汉诺塔问题:

//参考视频:正月点灯笼(汉诺塔)
#include <stdio.h>

int count=0;

//hanoi函数:hanoi(int n, char A, char B, char C)理解(将n个盘子从A经过B移动到C) 
void hanoi(int n, char A, char B, char C){
    if(n==1){
        printf("%c-->%c\n", A, C);
    }else{
        hanoi(n-1, A, C, B);
        printf("%c-->%c\n", A, C);
        hanoi(n-1, B, A, C);
    }
    count++;
}

int main(){
    hanoi(6, 'A', 'B', 'C');
    printf("一共移动%d次\n", count);
    return 0;
} 

6.归并排序:

//参考视频:正月点灯笼
#include <stdio.h>

void merge(int arr[], int L, int M, int R){//有序数组合并合并 
    int L_size = M-L;
    int R_size = R-M+1;
    int left[L_size];//有序数组left:0~4 
    int right[R_size];//有序数组right:4~7 
    int i, j, k;
    for(i=0; i<=R; i++){//为left和right数组赋值 
        if(i<M){
            left[i] = arr[i];    
        }
        else{
            right[i-M] = arr[i];
        }
    }
    i = 0, j = 0, k = L;//i为left数组起点;j为right数组起点;k为arr数组起点; 
    while(i<L_size && j<R_size){//比较大小 
        if(left[i] > right[j]){
            arr[k] = right[j];
            k++;
            j++;
        }else{
            arr[k] = left[i];
            k++;
            i++;
        }
    }
    while(i<L_size){//如果left数组有剩余 
        arr[k] = left[i];
        k++;
        i++;
    }
    while(j<R_size){//如果right数组有剩余 
        arr[k] = right[j];
        k++;
        j++;
    }
} 

void mergesort(int arr[], int L, int R){//归并排序 
    if( L==R ){//递归终止条件 
        return ;
    }else{//递归体 
        int M = (L+R)/2;//M=3=(0+7)/2; 
        mergesort(arr, L, M);
        mergesort(arr, M+1, R);
        merge(arr, L, M+1, R);//如果L=0,R=7;那么此时merge合并时候的M应该为4=(0+7)/2+1; 
    }
} 

int main(){
    //arr数组中0~4有序,4~7有序
    //归并排序:将2个有序数组合并成为一个有序数组 
    
    //测试merge函数 
    /*int arr[8] = {2, 8, 9, 10, 4, 5, 6, 7};
    int L = 0;
    int M = 4;
    int R = 7;
    printf("排序前:");
    for(int i=0; i<8; i++){
        printf("%d ",arr[i]);
    }
    printf("\n"); 
    merge(arr, L, M, R);
    printf("排序后:");
    for(int i=0; i<8; i++){
        printf("%d ",arr[i]);
    }*/
    
    //测试mergesort函数 
    int arr[8] = {2, 8, 9, 10, 4, 5, 6, 7};
    int L = 0;
    int R = 7;
    mergesort(arr, L, R);
    for(int i=0; i<8; i++){
        printf("%d ",arr[i]);
    }
    return 0;
} 

7.几种经典排序实现:

冒泡排序:

//参考视频:正月点灯笼
#include <stdio.h>

//一次冒泡 
void bubble(int arr[], int n){
    int i, temp;
    for(i=0; i<n-1; i++){
        if(arr[i]>arr[i+1]){
            temp = arr[i];
            arr[i] = arr[i+1];
            arr[i+1] = temp; 
        }
    }
} 

//冒泡排序 
void bubblesort(int arr[], int n){
    for(int i=n; i>=1; i--){
        bubble(arr, i);
    }
} 
 
//测试 
int main(){
    int arr[]={2,1,5,4,3,6};
    bubblesort(arr, 6);
    for(int i=0; i<6; i++){
        printf("%d ",arr[i]);
    }
    return 0;
}

选择排序:

//参考视频:正月点灯笼
#include <stdio.h>

//找到最大值的下标 
int findMaxPos(int arr[], int n){
    int max = arr[0];//max
    int pos = 0;//max的下标 
    int i;
    for(i=0; i<n; i++){
        if(arr[i]>max){
            max = arr[i];
            pos = i; 
        }
    }
    return pos;
} 

//选择排序
void selectsort(int arr[], int n){
    while(n>1){
        int pos = findMaxPos(arr, n);
        int temp = arr[pos];
        arr[pos] = arr[n-1];
        arr[n-1] = temp; 
        n--;
    }
} 
 
//测试 
int main(){
    int arr[]={2,1,5,4,3,6};
    selectsort(arr, 6);
    for(int i=0; i<6; i++){
        printf("%d ",arr[i]);
    }
    return 0;
} 

8.KMP算法:

//参考视频:正月点灯笼
#include <stdio.h>

//求next数组(s代表字符串,next代表next数组,n代表长度)
void next_table(char s[], int next[], int n){
    next[0] = 0;
    int len = 0;
    int i = 1;;
    while(i<n){
        if(s[i] == s[len]){
            len++;
            next[i] = len;
            i++;
        }
        else{
            if(len > 0){
                len = next[len-1];
            }
            else{
                next[i] = len;
                i++;
            }
        }
    }
} 

int main(){
    char s[] = "ABABCABAA";
    int next[9];
    int n = 9;
    next_table(s, next, n);
    for(int i=0; i<n; i++){
        printf("%d ", next[i]);
    }
    return 0;
} 

9.动态规划:

理论讲解推荐:https://www.bilibili.com/video/av16544031(强👍)

posted @ 2020-01-11 18:27  Kimishima-Kana  阅读(212)  评论(0编辑  收藏  举报