常用的算法思想总结

  对于计算机科学而言,算法是一个非常重要的概念。它是程序设计的灵魂,是将实际问题同解决该问题的计算机程序建立起联系的桥梁。接下来,我们来看看一些常用的算法思想。

(一)穷举法思想

  穷举法,又称为强力法。它是一种最为直接,实现最为简单,同时又最为耗时的一种解决实际问题的算法思想。

  基本思想:在可能的解空间中穷举出每一种可能的解,并对每一个可能解进行判断,从中得到问题的答案。

  使用穷举法思想解决实际问题,最关键的步骤是划定问题的解空间,并在该解空间中一一枚举每一个可能的解。这里有两点需要注意,一是解空间的划定必须保证覆盖问题的全部解,二是解空间集合及问题的解集一定是离散的集合,也就是说集合中的元素是可列的、有限的。

  穷举法用时间上的牺牲换来了解的全面性保证,因此穷举法的优势在于确保得到问题的全部解,而瓶颈在于运算效率十分低下。但是穷举法算法思想简单,易于实现,在解决一些规模不是很大的问题,使用穷举法不失为一种很好地选择。

  现在我们通过具体的实例来理解穷举法思想。

/**
 *  实例:寻找[1,100]之间的素数
 *
 */
#include <stdio.h>

/**
 *  判断n是否是素数,是则返回1,不是则返回0
 */
int isPrime(int n)
{
    int i = 0;
    for (i = 2; i < n; i++) {
        if (0 == n % i) {
            return 0;
        }
    }
    return 1;
}

/**
 *  寻找[low,high]之间的素数
 */
void getPrime(int low, int high)
{
    int i = 0;
    for (i = low; i <= high; i++) {
        if (isPrime(i)) {
            printf("%d ", i);
        }
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    int low = 0, high = 0;
    printf("Please input the domain for searching prime\n");
    printf("low limitation:");
    scanf("%d", &low);
    printf("high limitation:");
    scanf("%d", &high);
    printf("The whole primes in this domain are\n");
    getPrime(low, high);
    getchar();

    return 0;
}

程序运行结果:

(二)递归与分治思想

  递归与分治的算法思想往往是相伴而生的,它们在各类算法中使用非常频繁,应用递归和分治的算法思想有时可以设计出代码简洁且比较高效的算法来。

  在解决一些比较复杂的问题,特别是解决一些规模较大得问题时,常常将问题进行分解。具体来说,就是将一个规模较大的问题分割成规模较小的同类问题,然后将这些小问题的子问题逐个加以解决,最终也就将整个大问题解决了。这种思想称之为分治。在解决一些问题比较复杂、计算量庞大的问题时经常被用到。

  最为经典的使用分治思想设计的算法就是“折半查找算法”。折半查找算法利用了元素之间的顺序关系(有序序列),采用分而治之的策略,不断缩小问题的规模,每次都将问题的规模减小至上一次的一半。

  而递归思想也是一种常见的算法设计思想,所谓递归算法,就是一种直接或间接地调用原算法本身的一种算法。

  接下来我们通过实例代码来理解递归、分治思想。

分治思想:

/**
 *  有一个数组A[10],里面存放了10个整数,顺序递增
 *  A[10] = {2, 3, 5, 7, 8, 10, 12, 15, 19, 21}
 *
 */

#include <stdio.h>

int bin_search(int A[], int n, int key)
{
    int low = 0, high = 0, mid = 0;
    high = n - 1;
    while (low <= high) {
        mid = (low + high) / 2;
        if (A[mid] == key) { //查找成功,返回mid
            return mid;
        }
        if (A[mid] < key) { //在后半序列中查找
            low = mid + 1;
        }
        if (A[mid] > key) { //在前半序列中查找
            high = mid - 1;
        }
    }
    return -1; //查找失败
}

int main(int argc, const char * argv[]) {
    // insert code here...
    int A[10] = {2, 3, 5, 7, 8, 10, 12, 15, 19, 21};
    int i = 0, n = 0, addr = 0;
    printf("The contents of the Array A[10] are\n");
    for (i = 0; i < 10; i++) {
        printf("%d ",A[i]); //显示数组A中的内容
    }
    printf("\nPlease input a interger for search\n");
    scanf("%d", &n); //输入待查找得元素
    addr = bin_search(A, 10, n); //折半查找,返回该元素在数组中的下标
    if (-1 != addr) {
        printf("%d is at the %dth unit is array A\n", n, addr);
    }else{
        printf("There is no %d in array A\n", n); //查找失败
    }
    getchar();

    return 0;
}

运行结果:

递归思想

/**
 *  计算n的阶乘n!
 *
 */

#include <stdio.h>

int factorial(int n)
{
    if (0 == n) {
        return 1;
    }else{
        return n * factorial(n - 1);
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    int n = 0, result = 0;
    printf("Please input factorial number\n");
    scanf("%d", &n);
    result = factorial(n);
    printf("result is %d", result);
    getchar();

    return 0;
}

运行结果

(三)贪心算法思想

  贪心算法的思想非常简单且算法效率很高,在一些问题的解决上有着明显的优势。

  先来看一个生活中的例子。假设有3种硬币,面值分别为1元、5角、1角。这3种硬币各自的数量不限,现在要找给顾客3元6角钱,请问怎样找才能使得找给顾客的硬币数量最少呢?你也许会不假思索的说出答案:找给顾客3枚1元硬币,1枚5角硬币,1枚1角硬币。其实也可以找给顾客7枚5角硬币,1枚1角硬币。可是在这里不符合题意。在这里,我们下意识地应用了所谓贪心算法解决这个问题。

  所谓贪心算法,就是总是做出在当前看来是最好的选择的一种方法。以上述的题目为例,为了找给顾客的硬币数量最少,在选择硬币的面值时,当然是尽可能地选择面值大的硬币。因此,下意识地遵循了以下方案:

(1)首先找出一个面值不超过3元6角的最大硬币,即1元硬币。

(2)然后从3元6角中减去1元,得到2元6角,再找出一个面值不超过2元6角的最大硬币,即1元硬币。

(3)然后从2元6角中减去1元,得到1元6角,再找出一个面值不超过1元6角的最大硬币,即1元硬币。

(4)然后从1元6角中减去1元,得到6角,再找出一个面值不超过6角的最大硬币,即5角硬币。

(5)然后从6角中减去5角,得到1角,再找出一个面值不超过1角的最大硬币,即1角硬币。

(6)找零钱的过程结束。

这个过程就是一个典型的贪心算法思想。

  因此,不难看出应用贪心算法求解问题,并不从问题的整体最优上加以考虑,它所作出的每一步选择只是在某种意义上得局部最优选择。因此,严格意义上讲,要使用贪心算法求解问题,该问题应当具备以下性质。

(1)贪心选择性质

  所谓贪心选择性质,就是指所求解的问题的整体最优解可以通过一系列的局部最优解得到。所谓局部最优解,就是指在当前的状态下做出的最好选择。

(2)最优子结构性质

  当一个问题的最优解包含着它的子问题的最优解时,就称此问题具有最优子结构性质。

  我们经常使用的哈夫曼(Huffman Tree)编码算法,求解最小生成树的克鲁斯卡尔(Kruskal)算法和普利姆(Prim)算法,求解图的单源最短路径的迪克斯特拉(Dijkstra)算法都是基于贪心算法的思想设计的。

  下面,我们来通过实例代码来理解贪心算法思想。

/**
 *  最优装船问题
 *  有一批集装箱要装入一个载质量为C的货船中,每个集装箱的质量由用户自己输入指定,在货船的装载体积不限的前提下,如何装载集装箱才能尽可能多地将集装箱装入货船中。
 */
#include <stdio.h>

void sort(int w[], int t[], int n)
{
    int i = 0, j = 0, tmp = 0;
    //存放w[]中的内容,用于排序
    int *w_tmp = (int *)malloc(sizeof(int) * n);

    for (i = 0; i < n; i++) {
        t[i] = i;  //初始化数组t
    }
    for (i = 0; i < n; i++) {
        w_tmp[i] = w[i];
    }
    for (i = 0; i < n - 1; i++) { //冒泡排序
        for (j = 0; j < n - i - 1; j++) {
            if (w_tmp[j] > w_tmp[j+1]) {
                tmp = w_tmp[j];
                w_tmp[j] = w_tmp[j+1];
                w_tmp[j+1] = tmp;
                tmp = t[j];
                t[j] = t[j+1];
                t[j+1] = tmp;
            }
        }
    }
}

void Loading(int x[], int w[], int c, int n)
{
    int i = 0;
    //存放w[]的下标,如果t[i]、t[j]、i<j,则w[i]<=w[j]
    int *t = (int *)malloc(sizeof(int) * n);
    //排序,用数组t[[]存放w[]的下标
    sort(w, t, n);
    for (i = 0; i < n; i++) {
        x[i] = 0;  //初始化数组x[]
    }
    for (i = 0; i < n && w[t[i]] <= c; i++) {
        x[t[i]] = 1; //将第t[i]个集装箱装入货船中
        c = c - w[t[i]]; //变量c中存放货船的剩余载质量
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    int x[5], w[5], c = 0, i = 0;
    printf("Please input the maximum loading of the sheep\n");
    scanf("%d", &c); //
    printf("Please input the weight of FIVE box\n");
    for (i = 0; i < 5; i++) { //
        scanf("%d", &w[i]);
    }
    Loading(x, w, c, 5); //
    printf("The following boxes will be loaded\n");
    for (i = 0; i < 5; i++) { //
        if (1 == x[i]) {
            printf("BOX:%d ", i);
        }
    }
    getchar();

    return 0;
}

运行结果

  以上,就是对算法设计中几个常见的思想的总结。

时间: 2024-10-05 04:45:03

常用的算法思想总结的相关文章

基本算法思想Java实现的详细代码

基本算法思想Java实现的详细代码 算法是一个程序的灵魂,一个好的算法往往可以化繁为简,高效的求解问题.在程序设计中算法是独立于语言的,无论使用哪一种语言都可以使用这些算法,本文笔者将以Java语言为例介绍一些常用的算法思想. 分类 穷举算法思想 递推算法思想 递归算法思想 分治算法思想 概率算法思想  穷举算法思想 穷举算法的基本思想 从所有可能情况中搜索正确答案 1. 对于一种可能情况,计算其结果. 2. 判断结果是否满足,如不能满足者执行第一步来搜索下一个可能的情况:如满足则表示选找到一个

Java常用排序算法+程序员必须掌握的8大排序算法+二分法查找法

Java 常用排序算法/程序员必须掌握的 8大排序算法 本文由网络资料整理转载而来,如有问题,欢迎指正! 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排序(直接选择排序.堆排序) 4)归并排序 5)分配排序(基数排序) 所需辅助空间最多:归并排序 所需辅助空间最少:堆排序 平均速度最快:快速排序 不稳定:快速排序,希尔排序,堆排序. 先来看看 8种排序之间的关系: 1.直接插入排序 (1)基本思想:在要排序的一组数中,假设前面(n-1)[n>=2]

机器学习&amp;数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理)

http://www.cnblogs.com/tornadomeet/p/3395593.html 机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理) 前言: 找工作时(IT行业),除了常见的软件开发以外,机器学习岗位也可以当作是一个选择,不少计算机方向的研究生都会接触这个,如果你的研究方向是机器学习/数据挖掘之类,且又对其非常感兴趣的话,可以考虑考虑该岗位,毕竟在机器智能没达到人类水平之前,机器学习可以作为一种重要手段,而随着科技的不断发展,相信这方面的人才需求也会越来越大.

常用排序算法比较与分析

一.常用排序算法简述 下面主要从排序算法的基本概念.原理出发,分别从算法的时间复杂度.空间复杂度.算法的稳定性和速度等方面进行分析比较.依据待排序的问题大小(记录数量 n)的不同,排序过程中需要的存储器空间也不同,由此将排序算法分为两大类:[内排序].[外排序]. 内排序:指排序时数据元素全部存放在计算机的随机存储器RAM中. 外排序:待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中还需要对外存进行访问的排序过程. 先了解一下常见排序算法的分类关系(见图1-1) 图1-1 常见排

KMeans聚类算法思想与可视化

1.聚类分析 1.0 概念 聚类分析简称聚类(clustering),是一个把数据集划分成子集的过程,每一个子集是一个簇(cluster),使得簇中的样本彼此相似,但与其他簇中的样本不相似. 聚类分析不需要事先知道样本的类别,甚至不用知道类别个数,因此它是一种无监督的学习算法,一般用于数据探索,比如群组发现和离群点检测,还可以作为其他算法的预处理步骤. 下面的动图展示的是一个聚类过程,感受一下: 1.1 基本聚类方法 主要的聚类算法一般可以划分为以下几类: 方法 一般特点 划分方法 1.发现球形

javascript常用排序算法实现

毕业后,由于工作中很少需要自已去写一些排序,所以那些排序算法都忘得差不多了,不过排序是最基础的算法,还是不能落下啦,于是找了一些资料,然后用Javascript实现了一些常用的算法,具体代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>

五种常用的算法设计技巧之二:分治算法

一,介绍 分治算法主要包含两个步骤:分.治.分,就是递归地将原问题分解成小问题:治则是:在解决了各个小问题之后(各个击破之后)合并小问题的解,从而得到整个问题的解 二,分治递归表达式 分治算法一般都可以写出一个递归表达式:比如经典的归并排序的递归表达式:T(N)=2T(N/2)+O(N) T(N)代表整个原问题,采用了分治解决方案后,它可以表示成: ①分解成了两个规模只有原来一半(N/2)的子问题:T(N/2) ②当解决完这两个子问题T(N/2)之后,再合并这两个子问题需要的代价是 O(N) 递

常用排序算法实现[交换排序之冒泡排序、快速排序]

相关知识 1. 稳定排序和非稳定排序: 稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序. 如果排序算法是稳定的,就是当有两个有相等关键的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前. 2. 内排序和外排序 在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序: 在排序过程中,只有部分数被调入内存,并借助内存调整数在外存中的存放顺序排序方法称为外排序. 3.算法分类 排序算法从理论上分为如下几类: (1) 交换排序法:

javascript常用经典算法实例详解

javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与使用技巧,需要的朋友可以参考下 本文实例讲述了javascript常用算法.分享给大家供大家参考,具体如下: 入门级算法-线性查找-时间复杂度O(n)--相当于算法界中的HelloWorld ? 1 2 3 4 5 6 7 8 9 10 //线性搜索(入门HelloWorld) //A为数组,x为要