第十五节:贪心算法(下)

一 、 贪心算法的解题套路实战一(最多的会议宣讲场次)

1.1 描述

一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。

给你每一个项目开始的时间和结束的时间

你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。

返回最多的宣讲场次。

1.2 分析 在绝对环境下选择最优解

贪心 先按会议结束时间排序,然后在在数组里面找,先以前开始时间为0,找第一个会议可以结束的时间更新会议下一个可以开始的时间为当前这个会议的结束时间,那么下一个会议可以开始的时间要大于前面那个结束的时间

1.3 代码

// 会议的开始时间和结束时间,都是数值,不会 < 0
    public static int bestArrange2(Program[] programs) {
        Arrays.sort(programs, new ProgramComparator());
        int timeLine = 0;//会议的默认结束时间
        int result = 0;
        // 依次遍历每一个会议,结束时间早的会议先遍历
                //比如上一个会议的结束时间比下一个会议的开始时间小,那么下一个会议就可以在上一个会议结束的时候开始
        for (int i = 0; i < programs.length; i++) {
            if (timeLine <= programs[i].start) {
                result++;
                timeLine = programs[i].end;
            }
        }
        return result;
    }
//比较器
    public static class ProgramComparator implements Comparator<Program> {

        @Override
        public int compare(Program o1, Program o2) {
            return o1.end - o2.end;
        }

    }

二 、 贪心算法的解题套路实战二(分割的最小代价)

2.1 描述

一块金条切成两半,是需要花费和长度数值一样的铜板的。

比如长度为20的金条,不管怎么切,都要花费20个铜板。 一群人想整分整块金条,怎么分最省铜板?

例如,给定数组{10,20,30},代表一共三个人,整块金条长度为60,金条要分成10,20,30三个部分。

如果先把长度60的金条分成10和50,花费60;

再把长度50的金条分成20和30,花费50;

一共花费110铜板。

但如果先把长度60的金条分成30和30,花费60;再把长度30金条分成10和20, 花费30;一共花费90铜板。

输入一个数组,返回分割的最小代价。

2.2 分析

贪心算法解决输入一个数组返回金条分割的最小代价既哈夫曼树

分析

根据上面的列子可以看出,切的时候尽可能的向平均靠近,之后的切的代价就越小

流程 使用小跟堆,每次从堆里面弹出两个树合完放入小根堆,等堆排好序后又弹出两个树,当小跟堆里面只剩一个树的时候停止,形成如下的树的方案就是最优方案,代价就是圈里面的值加起来-这就是在创建一棵哈夫曼树!

2.3 代码


    public static int lessMoney2(int[] arr) {
        PriorityQueue<Integer> pQ = new PriorityQueue<>();
        for (int i = 0; i < arr.length; i++) {
            pQ.add(arr[i]);
        }
        int sum = 0;
        int cur = 0;
        while (pQ.size() > 1) {
            cur = pQ.poll() + pQ.poll();
            sum += cur;
            pQ.add(cur);
        }
        return sum;
    }

三  贪心算法的解题套路三 (根据下面的描述你最后获得的最大钱数)

3.1 描述

输入: 正数数组costs、正数数组profits、正数K、正数M

costs[i]表示i号项目的花费

profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润)

K表示你只能串行的最多做k个项目

M表示你初始的资金

说明: 每做完一个项目,马上获得的收益,可以支持你去做下一个项目。只能窜行的做项目,不能并行的做项目。

输出:你最后获得的最大钱数。
 

3.2 分析

第一步 第一轮

分析先把所有数据按照花费放到小根堆里面,然后再来一个大根堆,按照利润来组织;假如我的钱是2元,小根堆里面所有能做的项目弹出来进大根堆;(1,3)(2,5)弹出进大根堆在大根堆里面是(2,5)(1,3)这个时候做要做的项目就是大根堆的堆顶;这个时候的利润就是2(本金)+5(利润)=7,

第二轮来了,接着第一轮的过程,从小根堆里面弹出7元能做的项目到大根堆里面;

第二轮

根据钱数去解锁小根堆,到大根堆做堆顶的树,一直循环

3.3 代码

package class14;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Code04_IPO {

    // 最多K个项目
    // W是初始资金
    // Profits[] Capital[] 一定等长
    // 返回最终最大的资金
    public static int findMaximizedCapital(int K, int W, int[] Profits, int[] Capital) {
        PriorityQueue<Program> minCostQ = new PriorityQueue<>(new MinCostComparator());
        PriorityQueue<Program> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
        for (int i = 0; i < Profits.length; i++) {
            minCostQ.add(new Program(Profits[i], Capital[i]));
        }
        for (int i = 0; i < K; i++) {
            while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
                maxProfitQ.add(minCostQ.poll());
            }
            if (maxProfitQ.isEmpty()) {
                return W;
            }
            W += maxProfitQ.poll().p;
        }
        return W;
    }

    public static class Program {
        public int p;
        public int c;

        public Program(int p, int c) {
            this.p = p;
            this.c = c;
        }
    }

    public static class MinCostComparator implements Comparator<Program> {

        @Override
        public int compare(Program o1, Program o2) {
            return o1.c - o2.c;
        }

    }

    public static class MaxProfitComparator implements Comparator<Program> {

        @Override
        public int compare(Program o1, Program o2) {
            return o2.p - o1.p;
        }

    }

}

四 墙 路问题,需要多少灯

4.1 描述

给定一个字符串str,只由‘X’和‘.’两种字符构成。

‘X’表示墙,不能放灯,也不需要点亮

‘.’表示居民点,可以放灯,需要点亮

如果灯放在i位置,可以让i-1,i和i+1三个位置被点亮

返回如果点亮str中所有需要点亮的位置,至少需要几盏灯

4.1 分析 当前轮次对i的几种情况的处理

4.2 代码

五 并查集((并)又可以合并 (查)即可查找)

5.1 描述

如下先将自己设置成一个单独的集合,查找两个小样本是否全在一个大点的集合叫并查集,其实很多设计是可以做到这个功能的

比如 list

但是有100万个数的情况,需要将a的100万个合到b里面去,要倒100万次

所以有了以下的设计,并查集单独讲主要是他的性能非常的优异

开始的时候找一个指针指向自己,假如我们要找 a所在的集合a,e所在的集合是不是在一个集合,我怎么给你查,一开始肯定都不是,

那怎么查,a往上找找到不能再往上的节点是a,e往上找找到不能再往上的节点是e,我们把一个节点往上找,找到不能再往上的那个节点叫节点的代表节点,如下所是 a的代表节点是a,b的代表节点是b

下面我们要求a和e所在的集合给合并起来,同样找代表节点, a的代表节点是a,b的代表节点是b,建立一种机制,a所在的集合大小1,e的集合大小也是1,怎么变成一个结合,直接让e的指针往a上指,以后再找a和e是否是一个集合, a往上找不能再找了是a,e往上找不能再找了也是a

接着再玩,找e和c是不是一个集合,同样找e和c的代表集点,e是a,c是c,不在一个结合,怎么合并,此时a所在的集合大小是2,c所在的结合大小是1,这个时候怎么合并谁挂谁,小的去挂大的,e挂在a下,这个时候a,e,c的代节点都是a

5.2 分析

核心是我是拿代表节点属于那个集合

优化一

小挂大

优化二

HashMap替代指针

首先我们是可以通过a找到f的,但是我们返回之前做一个优化 让当前链上的节点变为扁平,下一次再查的时候一步到位

public HashMapV>, NodeV>> parents; 如下

while (!path.isEmpty()) {

parents.put(path.pop(), cur);

}

优化三

public HashMapV>, Integer> sizeMap;的含义 只有集合的代表节点才会在sizeMap里面留下记录

5.3 代码

public class Code05_UnionFind {

    public static class Node<V> {
        V value;

        public Node(V v) {
            value = v;
        }
    }

    public static class UnionFind<V> {
        public HashMap<V, Node<V>> nodes;
        public HashMap<Node<V>, Node<V>> parents;
        public HashMap<Node<V>, Integer> sizeMap;

        public UnionFind(List<V> values) {
            nodes = new HashMap<>();
            parents = new HashMap<>();
            sizeMap = new HashMap<>();
            for (V cur : values) {
                Node<V> node = new Node<>(cur);
                nodes.put(cur, node);
                parents.put(node, node);
                sizeMap.put(node, 1);
            }
        }

        // 给你一个节点,请你往上到不能再往上,把代表返回
        public Node<V> findFather(Node<V> cur) {
         //    栈
            Stack<Node<V>> path = new Stack<>();
            //最开始的时候是将自己设置为自己的parents node,所以这里这样判断
            while (cur != parents.get(cur)) {
                path.push(cur);
                cur = parents.get(cur);
            }
                //优化1
            while (!path.isEmpty()) {
                parents.put(path.pop(), cur);
            }
            return cur;
        }

        public boolean isSameSet(V a, V b) {
            return findFather(nodes.get(a)) == findFather(nodes.get(b));
        }

        public void union(V a, V b) {
      //优化2
            Node<V> aHead = findFather(nodes.get(a));
            Node<V> bHead = findFather(nodes.get(b));
            if (aHead != bHead) {
                int aSetSize = sizeMap.get(aHead);
                int bSetSize = sizeMap.get(bHead);
                Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
                Node<V> small = big == aHead ? bHead : aHead;
                parents.put(small, big);
                sizeMap.put(big, aSetSize + bSetSize);
    //优化三
                sizeMap.remove(small);
            }
        }

        public int sets() {
            return sizeMap.size();
        }

    }
}

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
背包问题的贪心算法,背包问题 ---- * 已知有n种物品和一个可容纳M重量的背包,每种物品i的重量是w[i]。假定将物品i的一部分x[i]放入背包就会得到p[i]x[i]的效益,这里, * 0<=x[i]<=1,p[i]>0.采用怎样的方法才能使装包的效益最大呢? * 考虑以下情况下的背包问题:n = 3,M = 20,(p0,p1,p2) = (25,24,15),(w0,w1,w2) = * (18,15,10).其中的4个可行解是 * (x0,x1,x2) w0x0 + w1x1 + w2x2 p0x0 + p1x1 + p2x2 * (1/2,1/3,1/4) 16.5 24.25 * (1,2/15,0) 20 28.2 * (0,2/3,1) 20 31 * (0,1,1/2) 20 31.5 * 在这4个可行解中第四个的效益值最大。 定理:如果 p1/w1>=p2/w2>=...>=pn/wn,则算法对于给定的背包问题实例生成一个最优解。 证明: * 设X= (x1,...,xn)是最优解。如果所有的xi = 1,显然这个解是最优解。于是,设j是使xj != 1 的最小下标。由算法可知,对于1<=i<=j * ,xi=1;对于 j<i<=n,xi =0;对于j, 0<=xj<1.如果X不是一个最优解,则必定存在一个可行解Y=(y1,...yn),使得 * piyi > pixi.不失 一般性,可以假定 wiyi =M.设k是使得yk!=xk的最小下标。显然,这样的k必定存在。由上面的假设,可以推得yk<xk. * 这可从3种可能发生的情况,即k<j,k=j,k>j分别得到证明: (1)若k<j,则xk = 1.因yk!=xk,从而yk<xk. (2)若k=j ,由于 ∑wjxi = * M,且对1<=i<j,有xi=yi=1,而对j<i<=n,有xi =0.若yk>xk,显然有∑wiyi>M,与Y是可行解矛盾。若yk=xk * ,与假设yk!=xk矛盾,故yk<xk. (3)若k>j,则∑wiyi>m,这是不可能的。 * 现在,假定把yk增加到xk,那么必须从(yk+1,...,yn)中减去同样多的量,使得所有的总容量仍是M。这导致一个新的解Z=(z1,...zn), * 其中,zi = xi , 1<=i<=k,并且∑(k<i<=n)wi(yi-zi)= wk(zk-yk).因此,对于Z有 * ∑pizi = ∑piyi + (zk-yk)wkpk/wk-∑(k<i<=n)(yi-zi)wipi/wi * >= ∑piyi +[(zk-yk)wk-∑(yi-zi)wi]pk/wk * = ∑piyi * 如果∑pizi>∑piyi,则Y不可能是最优解。如果这两个和数相等,同时Z=X,则X就是最优解;若Z!=X,则重复上面的讨论,或者证明Y不是最 * 优解,或者把Y转换成X,从而证明了X也是最优解。证毕。 */ public class BinSerch { //对数组buf降序排列 同时 index 数组记录排序前的数组索引 public static void order(double[] buf, int[] index) { int count = 1; while (count++ < buf.length) { for (int i = buf.length - 1; i > 0; i--) { if (buf[i] > buf[i - 1]) { double temp = buf[i]; buf[i] = buf[i - 1]; buf[i - 1] = temp; int temp1 = index[i]; index[i] = index[i - 1]; index[i - 1] = temp1; } else continue; } } for (int j = 0; j < buf.length; j++) { System.out.print(buf[j] + "(" + j + ")"); } System.out.println(); } public static void main(String[] args) { //对上述背包问题求最优解 int n = 3; //物品数量 double[] p = { 25, 24, 15 }; //效益数组 double[] w = { 18, 15, 10 }; //重量数组 double[] pw = { p[0] / w[0], p[1] / w[1], p[2] / w[2] }; //选取pi/wi为其量度标准 int[] index = { 0, 1, 2 }; //数组索引 double[] record = new double[3];//记录排序前数组下标 double cu = 20; //背包剩余容量 order(pw, index); //排序 //背包问题的贪心算法 int i = 0; for (i = 0; i < n; i++) { if (w[index[i]] < cu) { record[i] = 1; cu = cu - w[index[i]]; } else { break; } } if (i < n) { record[i] = cu / w[index[i]]; } for (int j = 0; j < record.length; j++) { System.out.print("x" + j + "\t"); System.out.print(record[j] + "\t"); } } }
一、本书的内容 目前,市面上有关计算机算法的书很多,有些叙述严谨但不全面,另外一些则是容量很大但不够严谨。本书将叙述的严谨性以及内容的深度和广度有机地结合了起来。第1版推出后,即在世界范围内受到了广泛的欢迎,被各高等院校用作多种课程的教材和业界的标准参考资料。它深入浅出地介绍了大量的算法及相关的数据结构,以及用于解决一些复杂计算问题的高级策略(如动态规划、贪心算法、平摊分析等),重点在于算法的分析和设计。对于每一个专题,作者都试图提供目前最新的研究成果及样例解答,并通过清晰的图示来说明算法的执行过程。. 本书是原书的第2版,在第1版的基础之上增加了一些新的内容,涉及算法的作用、概率分析和随机化算法、线性规划,以及对第1版中详尽的、几乎涉及到每一小的修订。这些修订看似细微,实际上非常重要。书中引入了“循环不变式”,并贯穿始终地用来证明算法的正确性。在不改动数学和分析重点的前提下,作者将第1版中的许多数学基础知识从第一部分移到了附录中。 二、本书的特点 本书在进行算法分析的过程中,保持了很好的数学严谨性。书中的分析和设计可以被具有各种水平的读者所理解。相对来说,每一章都可以作为一个相对独立的单元来教授或学习。书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如人类基因项目)、商业和工程等领域。每一小通常以对相关历史素材的讨论结束,讨论了在每一算法领域的原创研究。 本书的特点可以概括为以下几个方面: 1.概念清晰,广度、深度兼顾。 本书收集了现代计算机常用的数据结构和算法,并作了系统而深入的介绍。对涉及的概念和背景知识都作了清晰的阐述,有关的定理给出了完整的证明。 2.“五个一”的描述方法。 本书以相当的深度介绍了许多常用的数据结构和有效的算法。编写上采用了“五个一”,即一章介绍一个算法、一种设计技术、一个应用领域和一个相关话题。.. 3.图文并茂,可读性强。 书中的算法均以通俗易懂的语言进行说明,并采用了大量插图来说明算法是如何工作的,易于理解。 4.算法的“伪代码”形式简明实用。 书中的算法均以非常简明的“伪代码”形式来设计,可以很容易地把它转化为计算机程序,直接应用。 注重算法设计的效率,对所有的算法进行了仔细、精确的运行时间分析,有利于进一步改进算法。 三、本书的用法 本书对内容进行了精心的设计和安排,尽可能考虑到所有水平的读者。即使是初学计算机算法的人,也可以在本书中找到所需的材料。 每一章都是独立的,读者只需将注意力集中到最感兴趣的章阅读。 1.适合作为教材或教学参考书。 本书兼顾通用性与系统性,覆盖了许多方面的内容。本书不但阐述通俗、严谨,而且提供了大量练习和思考题。针对每一的内容,都给出了数量和难度不等的练习题。练习题用于考察对基本内容的掌握程度,思考题有一定的难度,需进行精心的研究,有时还通过思考题介绍一些新的知识。 前言回到顶部↑本书提供了对当代计算机算法研究的一个全面、综合性的介绍。书中给出了多个算法,并对它们进行了较为深入的分析,使得这些算法的设计和分析易于被各个层次的读者所理解。力求在不牺牲分析的深度和数学严密性的前提下,给出深入浅出的说明。. 书中每一章都给出了一个算法、一种算法设计技术、一个应用领域或一个相关的主题。算法是用英语和一种“伪代码”来描述的,任何有一点程序设计经验的人都能看得懂。书中给出了230多幅图,说明各个算法的工作过程。我们强调将算法的效率作为一种设计标准,对书中的所有算法,都给出了关于其运行时间的详细分析。 本书主要供本科生和研究生的算法或数据结构课程使用。因为书中讨论了算法设计中的工程问题及其数学性质,因此,本书也可以供专业技术人员自学之用。 本书是第2版。在这个版本里,我们对全书进行了更新。所做的改动从新增了若干章,到个别语句的改写。 致使用本书的教师 本书的设计目标是全面、适用于多种用途。它可用于若干课程,从本科生的数据结构课程到研究生的算法课程。由于书中给出的内容比较多,只讲一学期一般讲不完,因此,教师们应该将本书看成是一种“缓存区”或“瑞典式自助餐”,从中挑选出能最好地支持自己希望教授的课程的内容。 教师们会发现,要围绕自己所需的各个章来组织课程是比较容易的。书中的各章都是相对独立的,因此,你不必担心意想不到的或不必要的各章之间的依赖关系。每一章都是以为单位,内容由易到难。如果将本书用于本科生的课程,可以选用每一章的前面几内容;在研究生课程中,则可以完整地讲授每一章。 全书包含920多个练习题和140多个思考题。每一结束时给出练习题,每一章结束时给出一些
基础篇 1、 算法有哪些特点?它有哪些特征?它和程序的主要区别是什么? 2、 算法的时间复杂度指的是什么?如何表示? 3、 算法的空间复杂度指的是什么?如何表示? 4、 什么是最坏时间复杂性?什么是最好时间复杂性? 5、 什么是递归算法?什么是递归函数? 6、 分治法的设计思想是什么? 7、 动态规划基本步骤是什么? 8、 回溯法与分枝限界法之间的相同点是什么?不同之处在哪些方面? 9、 分枝限界法的基本思想是什么? 10、 限界函数的功能是什么? 11、 设某一函数定义如下: 编写一个递归函数计算给定x的M(x)的值。 12、 已知一个顺序表中的元素按元素值非递减有序排列,编写一个函数删除表中多余的值相同的元素。 13、 分别写出求二叉树结点总数及叶子总数的算法。 分治术 14、 有金币15枚,已知其中有一枚是假的,而且它的重量比真币轻。要求用一个天平将假的金币找出来,试设计一种算法(方案),使在最坏情况下用天平的次数最少。 15、 利用分治策略,在n个不同元素中找出第k个最小元素。 16、 设有n个运动员要进行网球循环赛。设计一个满足以下要求的比赛日程表。 (1)每个选手必须与其它n-1选手各赛一次; (2)每个选手一天只能赛一次。 17、 已知序列{503,87,512,61,908,170,897,275,652,462},写一个自底向上的归并分类算法对该序列作升序排序,写出算法中每一次归并执行的结果。 贪心法 18、 设有n个文件f1,f2,…,fn要求存放在一个磁盘上,每个文件占磁盘上1个磁道。这n个文件的检索概率分别是p1,p2,…,pn,且 =1。磁头从当前磁道移到被检索信息磁道所需的时间可用这两个磁道之间的径向距离来度量。如果文件fi存放在第i道上,1≤i≤n则检索这n个文件的期望时间是 。其中d(i,j)是第i道与第j道之间的径向距离。磁盘文件的最优存储问题要求确定这n个文件在磁盘上的存储位置,使期望检索时间达到最小。试设计一个解此问题的算法,并分析算法的正确性与计算复杂性。 19、 设有n个正整数,编写一个算法将他们连接成一排,组成一个最大的多位整数。用贪心法求解本题。 20、 键盘输入一个高精度的正整数N(此整数中没有‘0’),去掉其中任意S个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数最小(输出应包括所去掉的数字的位置和组成的新的正整数,N不超过240位)。 21、 对于下图给出的有向网,写出用Dijkstra方法求从顶点A到图中其它顶点的最短路径的算法,并写出执行算法过程中顶点的求解次序及从顶点A到各顶点路径的长度。 22、 对于上图给出的有向图,写出最小成本生成树,给出求解算法。 动态规划 23、 求出上图中每对结点间的最短距离的算法,并给出计算结果。 24、 下图中给出了一个地图,地图中每个顶点代表一个城市,两个城市间的连线代表道路,连线上的数值代表道路的长度。现在,想从城市A到达城市E,怎样走路程最短,最短路程的长度是多少? 25、 已知序列a1,a2,…,an,试设计一算法,从中找出一子序列 ai1 < ai2 < … E。试用动态规划的最优化原理求出A->E的最省费用。 29、 已知如下图,写出用动态规划求最短路径的递推关系式,并写出求从源点A0到终点A3 的最短路径过程。给出求解算法。 6 A1 A2 5 5 2 A0 A3 3 4 4 B1 B2 5 搜索与遍历问题 30、 已知有向图G=,试设计一算法以判断对于任意两点u和v,是否存在一条从u到v的路径,并分析其复杂度。 31、 对于给定的一个二叉树T(如下图) a) 设计一个算法,统计二叉树中结点总数; b) 设计一个算法,求二叉树最大宽度及最大宽度所在深度。 32、 判近亲问题。给定一个家族族谱,为简化问题起见,假设家族中的夫妻关系只表示男性成员。设用线性表存储家族成员,用成员的父指针指向其生父。编写一个在此种族谱表示方式下的算法,判断给定的二个家族成员是否是五代内的近亲。(提示:家族成员的表示方式应与搜索方式相适应。) 33、 完全二叉树定义为:深度为K,具有N个结点的二叉树的每个结点都与深度为K的满二叉树中编号从1至N的结点一一对应。(1)写一个建立二叉树的算法。(2)写一个判别给定的二叉树是否是完全二叉树的算法。 34、 编写计算整个二叉树高度的算法(二叉树的高度也叫二叉树的深度)。 35、 编写计算二叉树最大宽度的算法(二叉树的最大宽度是指二叉树所有层中结点个数的最大值)。 回溯法 36、 (组合问题)求出从自然数1,2,…,n中任取r个数的所有组合。 37、 传教士与野人渡河问题。有M个传教士和M个野人准备渡河,船一次最多载2人,任何时刻野人数不能多于传教士数,但允许全部为野人。编写算法给出合理的渡河计划。 38、 某乡有n个村庄,有一个售货员,他要到各个村庄去售货,各村庄之间的路程s是已知的,且A村到B村与B村到A村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1。试设计一个算法,帮他选择一条最短的路。 39、 设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设wi,j是从供应商j处购得的部件i的重量,ci,j是相应的价格。试设计一个算法,给出总价格不超过c的最小重量机器设计。 40、 设有n件工作分配给n个人。为第i个人分配工作j所需的费用为ci,j 。试设计一个算法,计算最佳工作分配方案,为每一个人都分配1 件不同的工作,并使总费用达到最小。 41、 编写程序求解骑士巡游问题:在n行n列的棋盘上(如n=8),假设一位骑士(按象棋中“马走日”的行走法)从初始坐标位置(x1,y1)出发,要遍访(巡游)棋盘中的每一个位置一次。请编一个程序,为骑士求解巡游“路线图”(或告诉骑士,从某位置出发时,无法遍访整个棋盘 — 问题无解骑士巡游)。
第1章向读者介绍数据结构作为数据集合的概念。介绍线性和非线性集合的概念。示范说明了Collection类。本章还介绍泛型编程的概念。泛型编程允许程序员编写一个类或一种方法,并且把它用于众多数据类型。泛型编程是C#语言一种重要的新特性(在C#2.0以及更高版本中可用)。这种特性是如此重要以至于在System.Collections.Generic命名空间中存在一个专门的泛型数据结构库。当数据结构具有在此库中能找到的泛型实现时,就会讨论它的用途。本章结尾处介绍了衡量书中讨论的数据结构与算法性能的方法。 第2章提供了数组构造方法的回顾,并连同示例说明了Array类的特征。Array类把许多与数组相关的函数(UBound函数、LBound函数等等)封装到单独一个包中。ArrayLists是数组的一种特殊类型,它支持动态地调整容量。 第3章是对基础排序算法的介绍,例如冒泡排序和插入排序。而第4章则研究了用于内存查找的最基本算法,顺序查找和二叉查找。 第5章探讨了两种经典的数据结构:堆栈和队列。本章强调的重点是这些数据结构在解决日常数据处理问题中的实际应用。第6章讲述了BitArray类。这种类可以用于有效地表示大量整数值,比如测试成绩。 数据结构的书中通常不包含字符串,但是第7章介绍了字符串、String类和StringBuilder类。这是因为在C#语言中许多的数据处理是在字符串上执行的,读者应该接触基于这两种类的特殊方法。第8章分析了用于文本处理和模式匹配的正则表达式的使用。与较传统的字符串函数和方法相比,正则表达式常常会提供更强大更有效的处理。 第9章向读者介绍作为数据结构的字典的使用。字典和基于字典的不同数据结构把数据作为键/值对来存储。本章向读者说明了如何创建基于DictionaryBase类的他或她自己的类。DictionaryBase类是一个抽象类。第10章包括散列表和HashTable类。HashTable类是字典的一种特殊类型,它用散列算法对内部数据进行存储。 链表作为另外一种经典的数据结构是在第11章介绍。链表在C#语言中不像在C++这样基于指针的语言中那样重要,但是它始终在C#编程中发挥作用。第12章为读者介绍另一种经典数据结构——二叉树。二叉查找树作为二叉树的特殊类型将是本章的主要内容。其他二叉树类型在第15章进行介绍。 第13章向读者说明在集合中存储数据的方法。这种方法在数据结构只存储唯一数据值的情况下是很实用的。第14章涵盖了高级排序算法,包括流行且高效的快速排序算法。此算法是大多数在.NET框架库中实现的排序程序的基础。第15章会看到三种数据结构。在无法使用二叉查找树的时候,这三种数据结构证明对查找是很有用的。他们是:AVL树、红黑树和跳跃表。 第16章讨论了图以及图的算法。图在表示许多不同的数据类型时非常有用,特别是网络的情况。最后,第17章向读者介绍真正的算法设计技巧是什么:动态算法贪心算法
出版者的话 专家指导委员会 译者序 前言 第一部分 基础知识 引言 第1章 算法在计算中的作用 1.1 算法 1.2 作为一种技术的算法 第2章 算法入门 2.1 插入排序 2.2 算法分析 2.3 算法设计 2.3.1 分治法 2.3.2 分治法分析 第3章 函数的增长 3.1 渐近记号 3.2 标准记号和常用函数 第4章 传归式 4.1 代换法 4.2 递归树方法 4.3 主方法 4.4 主定理的证明 4.4.1 取正合幂时的证明 4.4.2 上取整函数和下取整函数 第5章 概率分析和随机算法 5.1 雇用问题 5.2 指示器随机变量 5.3 随机算法 5.4 概率分析和指示器随机变量的进一步使用 5.4.1 生日悖论 5.4.2 球与盒子 5.4.3 序列 …… 第二部分 排序和统计学 引言 第6章 堆排序 第7章 快速排序 第8章 线性时间排序 第9章 中位数和顺序统计学 第三部分 数据结构 第10章 基本数据结构 第11章 散列表 第12章 二叉查找树 第13章 红黑树 第14章 数据结构的扩张 第四部分 高级设计和分析技术 导论 第15章 动态规划 第16章 贪心算法 第17章 平摊分析 第五部分 高级数据结构 概述 第18章 B树 第19章 二项堆 第20章 斐波那契堆 第21章 用于不相交集合的数据结构 第六部分 图算法 引言 第22章 图的基本算法 第23章 最小生成树 第24章 单源最短路径 第25章 每对项点间的最短路径 第26章 最大流 第七部分 算法研究问题选编 引言 第27章 排序网络 第28章 矩阵运算 第29章 线性规划 第30章 多项式与快速傅里叶变换 第31章 有关数论的算法 第32章 字符串匹配 第33章 计算几何学 第34章 NP完全性 第35章 近似算法 第八部分 附录:数学基础知识 引言 A 求和 B 集合等离散数学结构 C 计数和概率 参考文献 索引

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值