第一章 引论
学习数据结构的重要性:对于大量输入的运算性能的重要性
目录
- 1.1 本书讨论的内容
- 1.2 数学知识复习
- 1.3 递归简论
- 1.4 实现泛型特性构件 pre - Java 5
- 1.5 利用Java 5泛性实现泛型特性成分
1.1 本书讨论的内容
- 应用合理的算法 与 普通的算法解决问题的"差异"。
1.1.1 选择问题
设有一组N个数而要确定其中第k个最大者。我们称之为选择问题。
- 方法一:
将这N个数读进一个数组中,通过某种简单的算法,比如冒泡排序,以递减顺序将数组排序,然后返回位置k上的元素。
/**
* 选择问题:设有一组N个数而要确定其中第k个最大者 方法: 使用冒泡排序,然后返回位置 k-1 上的元素
*/
@Test
public void No1_1_1() {
//获取全局变量数组的克隆数组。
int array[] = mdzz_array.clone();
int m = (int) System.currentTimeMillis();
//排序
MyUtils.sort(array);
int k = array.length / 2;
System.out.println(array[k - 1]);
//计算结束时间
int n = (int) System.currentTimeMillis();
System.out.println(n - m);
}
- 方法二:
稍微好一点的算法就可以先把前k个元素读入数组并(以递减的顺序)进行排序。接着将剩下的元素逐个读入,当新元素被读到时,如果它小于等于该数组的第k个元素则忽略之,否则就将其放到数组的正确位置上。同时将数组中的一个元素挤出数组
/**
* 第二种方法 先把前k个元素读入数组并(递减排序).接着将剩下的元素逐个读入。
* 当新元素被读到时,如果它小于数组中的第k个元素则忽略之,否则将其放到数组中的正确的位置上, 同时将数组中的一个元素挤出数组。
*/
// @Test
public void No1_1_1_2() {
System.out.println("================================");
//获取全局变量数组的克隆数组。
int array[] = mdzz_array.clone();
int n = (int) System.currentTimeMillis();
int k = array.length / 2;
//填充数组
int array_copy[] = new int[k];
for (int i = 0; i < k; i++) {
array_copy[i] = array[i];
}
//排序
MyUtils.sort(array_copy);
for (int i = k; i < array.length; i++) {
if (array[i] > array_copy[array_copy.length - 1]) {
MyUtils.insertToArray(array[i], array_copy);
}
}
//计算程序结束时间
int m = (int) System.currentTimeMillis();
//打印
System.out.println(array_copy[k - 1]);
System.out.println(m - n);
}
全局变量
static int mdzz_array[];
static {
//生成一个十万长度的随机数组
mdzz_array = MyUtils.upSetRandom(100000);
}
运行结果
分析
- 当数据量达到10w的时候,两个方法都能运行出正常结果,但是方法二时间明显小于方法一的时间
- 当数据量达到100w的时候,两个方法都不能在短时间内运行出结果。(肯定会有更好的方法去解决,这个放到后面去讲解,我暂时也没学到233333)
1.1.2 解决流行的字谜
输入是由一些字母构成的一个二维数组以及一组单词组成。目标是要找出字谜中的单词,这些单词可能是水平的、垂直或沿对角线上任何方向放置的。
该图,字谜是由单词this、two、fat组成。
思路
- 通过循环,对该二维数组上的每一个点进行遍历,八个方向开始。如果有形成单词的,则放到答案队列当中。
- 改进:如果我们知道字典中最小的单词长度的话,那么我们遍历某个点的某个方向的时候,判断这个方向构成单词的最大长度如果小于字典中最小单词的长度的话。那么就可以过滤掉该方向。增加算法的效率。
代码
本章讲解思想,代码不是很重要,所以这种篇幅过长的代码就放到附件去了。主要是思路,思路对了代码没多长时间就自己敲出来了。
1.3 递归
程序调用自身的编程技巧称为递归( recursion)。
递归的两个基本准则:
- 基准情形(base case)。必须总要有某些基准的情形,他们不用递归就能求解。
- 不断推进(making progress)。对于那些要递归求解的情形,递归调用必须总能朝着一个基准情形推进。
1.3.1 案例1、阶乘
可能学递归都接触过这个阶乘问题,这个问题可能有些人都嚼烂了,随手一打就出来了。但是,这个思想还是蛮重要的,化繁为简。
public int jiecheng(int n){
if(n==1||n==0){
return 1;
}else{
return n * jiecheng(n-1);
}
}
(↑ 这么简单的案例,糊弄谁呢)
1.3.2 案例2、全排列
(对不起,打扰了)
分析与思路:
-
对于这个问题,我们首先在纸上画一画,大家想的思路应该都是,先固定"a",然后输出"b","c",再将"b","c"交换,之后再输出"acb"。b,c也是先固定再让剩下的交换。
- 那四个数呢,"abcd",一样的,先固定a ,再 固定 b 然后输出 abcd,交换c d,输出 abdc 在固定a,c输出 acbd……
实现方法:
@Test
public void No1_1_4() {
permute("xyz");
}
public void permute(String str) {
char[] c_str = str.toCharArray();
permute(c_str, 0, c_str.length - 1);
}
public void permute(char[] str, int low, int high) {
if (low == high) {
System.out.println(String.valueOf(str));
} else {
for (int i = low; i < str.length; i++) {
swap(str, low, i);
permute(str, low+1, high);
swap(str, i, low);
}
}
}
public void swap(char[]str,int i,int j){
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
总结
本章总体上来讲了算法的一些优点,还有一些数学和Java的泛型基础知识,就不在这里进行讲解了。