博文视点答题
1. 二维数组中的查找
1.1. 问题描述
在一个二维整数数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
例如下面的二维数组就是每行、每列都递增排序。如果在这个数组中查找数字7,则返回true;如果查找数字5,由于数组不含有该数字,则返回false。
图1-1
1.2. 问题分析
由于数组M是一个m*n阶矩阵。矩阵M的可能情况如下:
矩阵M的特点是:
(1) 图中“
红色元素”是以第一个元素为顶点的最大正方形对角线上的元素,这条对角线上的红色元素把矩阵的每行和每列都切割成了两部分。
(2) 行和列都是递增数列,“红色元素”所在行的左侧都比它小,右侧都比它大;所在列的上侧都比它小,下侧都比它大。
(3)矩阵M的第一个元素最小,最后一个元素最大。
算法描述:
(1)将key分别与矩阵的最大元素和最小元素比较,如果key比矩阵的最大元素大或者比最小元素小,则无须继续查找,不存在这样的key。
(2)以“红色元素”为分割点,对于行数大于等于列数的矩阵采用
列二分搜索(如图1-2和图1-4所示矩阵)。如果key等于“红色元素”返回true;比“红色元素”大在列下侧搜索;比“红色元素”小,则key只可能在图中红色箭头围成的区域中,把这样的区域称为“
最小区域”。发现“最小区域”后,直接进入最小区域搜索。
(3)对于行数小于列数的矩阵采用
行二分搜索(如图1-3所示矩阵)。
1.3. 算法实现
1.3.1. 行搜索算法
boolean rowSearch(int [][] matrix,int key) {
int row = matrix.length-1,col = matrix[0].length-1;
int low = 0 , high = 0,mid = 0,midVal = 0,fixMin=-1;
for (int i = 0; i <= row ; i++) {
if(fixMin != -1) {
low = 0;
high = fixMin;
} else {
if(key > matrix[i][i]) {
low = i+1;
high = col;
} else if(key < matrix[i][i]) {
low = 0;
high = i-1;
fixMin = high;//发现最小区域
} else {
return true;
}
}
while(low <= high) {
mid = (low + high)>>1;
midVal = matrix[i][mid];
if(key < midVal) {
high = mid -1;
} else if (key > midVal) {
low = mid + 1;
} else {
return true;//发现key
}
}
}
return false;//没有发现key
}
1.3.2. 列搜索算法
boolean colSearch(int [][] matrix,int key) {
int row = matrix.length-1,col = matrix[0].length-1;
int low = 0 , high = 0,mid = 0,midVal = 0,fixMin=-1;
for (int i = 0; i <= col ; i++) {
if(fixMin != -1) {
low = 0;
high = fixMin;
} else {
if(key > matrix[i][i]) {
low = i+1;
high = row;
} else if(key < matrix[i][i]) {
low = 0;
high = i-1;
fixMin = high;//发现最小区域
} else {
return true;
}
}
while(low <= high) {
mid = (low + high)>>1;
midVal = matrix[mid][i];
if(key < midVal) {
high = mid -1;
} else if (key > midVal) {
low = mid + 1;
} else {
return true;//发现key
}
}
}
return false;//没有发现key
}
1.3.3. 搜索主算法
boolean search(int [][] matrix,int key) {
//比最小值小
if(key<matrix[0][0]) {
return false ;
}
int row = matrix.length-1,col = matrix[0].length-1;
//比最大值大
if(key > matrix[row][col]) {
return false ;
}
if( row < col) {
//行数小于列数采用行搜索
return rowSearch(matrix, key);
} else {
//行数大于等于列数采用列搜索
return colSearch(matrix, key);
}
}
2. 青蛙跳台阶问题
2.1. 问题描述
(1) 一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级。求该青蛙跳上一个n 级的台阶总共有多少种跳法?(2)一只青蛙一次可以跳上1级台阶,也可以跳上2 级……它也可以跳上n 级,此时该青蛙跳上一个n级的台阶总共有多少种跳法?
2.2. 问题分析
对于n步台阶,记青蛙的跳法为F(n),n=S1+S2(其中Si表示一次连跳i步的次数.
当n=1时,(1,0) →F(n) = 1;
当n=2时,(0,1),(2,0) →F(n) = 2;
当n=3时,(1,1)[2], (3,0) →F(n) = 3;
青蛙可以先跳一步,再跳一次两连步;或者先跳一次两连步,再跳一次一步,故共计3次。
当n=4时,(0,2),(2,1)[3], (4,0) →F(n) = 5;
由上面的推到可知:
F(1) = 1; F(2) = 2;
F(3) = F(1) + F(2) = 1+2 = 3;
F(4) = F(2) + F(3) = 2+3 = 5;
.......
假设青蛙最后一次跳1步,则青蛙的跳法与只有n-1步台阶时跳法数一致,即有F(n-1) 种跳法;
假设最后一次跳2步,则青蛙的跳法与只有n-2步台阶时跳法数一致,即有
F(n-2) 种跳法。这里实际存在
对偶关系:m+(n-m) = n ,则F(m)= F(n-m)。所以,F(n)= F(n-1) + F(n-2)。
则:
由此可见青蛙的跳法是一个
斐波那契数列。
如果青蛙有m种跳法,即青蛙可以一次连跳1,2,3,4,5,...m阶台阶。由上面的推理过程得到:F(n)= F(n-1) + F(n-2)+...+ F(n-m).
如果 n-m < 0, F(n-m) = 0,青蛙一次能够跳跃的步数m大于台阶数n时,这样的跳法实际不存在。所以当n<m时,让m等于n。
n-m = 0, F(n-m) = 1, 即 F(0) = 1,表示青蛙一次能够跳n步台阶。
n-m = 1, F(n-m) = 1, 即 F(1) = 1。表示青蛙先跳一次再连跳m次。
对n >= 2时,对F(n)进行简化如下:
F(n-1)= F(n-2) + F(n-3)+...+ F(n-1-m).
F(n)- F(n-1) = F(n-1)- F(n-1-m).
则F(n)=2 F(n-1) - F(n-1-m).
当n <= m时,n – 1 – m < 0,F(n-1-m) = 0,则F(n)=2 F(n-1)。
F(1) = 1; F(2) = 2; F(3) = 4;…;F(n)=2^(n-1)。则:
2.3. 算法实现
2.3.1.1. 问题一的递归算法
int jump2(int n) {
if(n == 1 || n == 2) {
return n;
}
return jump2(n-1)+jump2(n-2);
}
2.3.1.2. 问题一的非递归算法
int jump2(int n) {
if(n == 1 || n == 2) {
return n;
}
int n1 = 1,n2 = 2;
int r = 0;
for (int i = 3; i <= n; i++) {
r = n1 + n2 ;
n1 = n2;
n2 = r ;
}
return r;
}
2.3.2.1. 问题二的递归算法
int jumpm0(int n,int m) {
if(n == 0) {
return 1;
}
if(n <= m) {
return 1<<(n-1);
}
return 2*jumpm0(n-1,m)-jumpm0(n-1-m,m);
}
2.3.3.2. 问题二的非递归算法
int jumpm(int n,int m) {
if(n <= m) {
return 1<<(n-1);
}
int [] f = new int [1 + m];
f[0] = 1;
//求f[1]...f[m]
for (int i = 0; i < m; ) {
f[++i] = 1<<i;
}
//求fn
int r = f[m];
for (int i = m+1; i < n;i++) {
r = 2 * f[m] - f[0];
f = poll(f);
f[m] = r;
}
r = 2 * f[m] - f[0];
return r;
}
int [] poll( int [] m) {
int len = m.length-1;
for (int i = 0; i < len ;) {
m[i] = m[++i];
}
return m;
}
图2-1
非递归算法通过增加辅助存储空间来存储F(n-1-m),.., F(n-2), F(n-1)。首先通过公式F(n)= F(n-1) + F(n-2)+...+ F(n-m)。求出F(0),…, F(1), F(m)。然后通过迭代求F(n)。迭代过程中通过公式F(n)=2 F(n-1) - F(n-1-m),求F(n)。每次迭代都需要像图2-1那样移动元素。
2.3.3.3. 问题二的非递归算法的改进
问题二的非递归算法中,求F(n)的迭代过程中用到了poll函数,这样每迭代一次数组f都需要整体向做移动一次,效率比较低。如果把数组f中的元素存到一个队列中,每次迭代实际上只与第一个元素和最后一个元素有关。迭代过程中移除首元素之后取出其值和最后一个元素的值计算出F(i)。再将F(i)插入到队尾。这样减少了数组的移动,提高了效率。但需要消耗一定的空间创建对象实例。以空间换取效率。改进后的
存储模型如下:
图2-2
改进后的java代码如下:
int jumpmm(int n,int m) {
if(n <= m) {
return 1<<(n-1);
}
//将f[0]...f[m]存放到队列中
Deque<Integer> fq = new LinkedList<Integer>();
fq.add(Integer.valueOf(1));
for (int i = 0; i < m; i++) {
fq.add(Integer.valueOf(1<<i));
}
//求fn
int r = 0;
for (int i = m+1; i < n;i++) {
r = 2 * fq.peekLast() - fq.pollFirst();
fq.add(Integer.valueOf(r));
}
r = 2 * fq.peekLast() - fq.pollFirst();
return r;
}
2.3.3.4. 问题二的运行结果
图2-3
3. 总结
通过本文可以了解
二分查找算法的实现;
递归算法向
迭代转化的方法;问题的
分析和推演方法;同时还可以了解java中队列的使用方法。在实际问题中
针对问题本身的特征对问题进行分析是一条很重要的法则,仔细阅读本文就可以看到本文的分析与算法都是基于问题本身的特征的。
4. 附件
附件中有本文的word,pdf详细文档和java代码。
附件《ch02.pdf》有第一题的一种巧妙解法。供大家学习参考。在pdf的第19页。
- 大小: 2.2 KB
- 大小: 2.5 KB
- 大小: 5.8 KB
- 大小: 4.1 KB
- 大小: 14.9 KB
- 大小: 7.7 KB
- 大小: 2.9 KB
分享到:
相关推荐
在描述中提到的博文链接,虽然没有提供具体内容,但通常博主会分享如何使用`<iterate>`标签以及在实际开发中的应用案例。通过访问该链接(https://duqiangcise.iteye.com/blog/286764),我们可以深入学习关于`...
还可能包括表格(<table>)、列表(<ul>、<ol>、<li>)、表单(<form>)、按钮(<button>)、输入字段(<input>)等交互元素的使用。 学习HTML时,通常会从以下几个方面进行: 1. **HTML基础知识**:了解HTML的...
竞赛题目#include <cstdio> #include <cstring> #include <ctype.h> #include <cstdlib> #include <cmath> #include <climits> #include <ctime> #include <iostream> #include <algorithm> #include <deque> #...
比如<h1>至<h6>定义了六级标题,<p>用于段落,<a>定义超链接,<img>插入图像,<ul>和<li>创建无序列表,<ol>和<li>创建有序列表,<div>作为内容分组元素等。 4. CSS样式:虽然“一个简单的网页”没有明确提到CSS,...
<th>标题1</th> <th>标题2</th> <th>标题3</th> <th>标题4</th> </tr> <tr> <td colspan="2">合并两列内容</td> <td>正常单元格</td> <td>正常单元格</td> </tr> <tr> <td>正常单元格</td> <td rowspan...
1. `<h1>`到`<h6>`标题元素,用于定义页面的主要标题和子标题。 2. `<nav>`元素,用于包含页面的主要导航链接。 3. `<img>`元素,用于显示logo或其他图像,通常与品牌关联。 4. `<form>`元素,尤其是搜索表单,使...
4. **表格与表单**:`<table>`标签用于创建表格,而`<form>`、`<input>`、`<button>`等则用于创建用户交互的表单,收集用户数据。 5. **布局与样式**:虽然HTML主要用于内容结构,但也有如`<br>`换行,`<hr>`水平线...
<li>列1</li> <li>列2</li> </ul> </li> <li> <ul class="table-row"> <li>行2,列1</li> <li>行2,列2</li> </ul> </li> </ul> ``` 接下来,我们需要为这些元素添加CSS样式来达到表格的效果。可以使用`...
<th>姓名</th> <th>年龄</th> <th>城市</th> </tr> </thead> <tbody> <tr> <td>张三</td> <td>25</td> <td>北京</td> </tr> <tr> <td>李四</td> <td>30</td> <td>上海</td> </tr> </tbody> </table...
1. `<if>`标签:用于判断条件,如果条件为真,则包含它的内容会被插入到SQL语句中。例如,当需要根据用户输入是否为空来决定是否添加某个查询条件时,可以使用`<if>`标签。 ```xml <if test="name != null"> AND ...
1. **语义性**:`<section>`元素的引入是为了增强HTML5的语义性,它不是一个布局元素,而是一个内容组织元素。因此,应避免仅仅为了样式或者布局需求而使用它。 2. **标题关联**:虽然`<section>`元素不强制要求...
<systemPath>C:\Users\Lenovo\.m2\repository\jacob\jacob.jar</systemPath> </dependency> ``` 注意,这里`<systemPath>`中的路径应当替换为你实际存放`jacob.jar`的位置。 3. **处理跨环境问题** - **解决...
<version>3.x.x</version> <!-- 请替换为当前版本 --> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> ...
在HTML的初级阶段,通常会学习基本标签如`<html>`、`<head>`、`<body>`、`<title>`等,以及段落`<p>`、标题`<h1>`至`<h6>`、链接`<a>`、图像`<img>`等元素的使用。 在进阶阶段,比如HTML4到HTML5的过渡,重点可能会...
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop> <!-- 其他Hibernate属性 --> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm...
3. 表格与列表:掌握`<table>`、`<tr>`、`<td>`用于创建表格,`<ul>`、`<ol>`和`<li>`用于创建无序列表和有序列表。 4. 表单元素:学习`<form>`、`<input>`、`<textarea>`、`<select>`和`<option>`等表单元素,用于...
1. **基本结构**:`<div>`标签是成对出现的,使用`<div>`开始并使用`</div>`结束,所有在这两个标签之间的内容都将被包含在这个分组中。 2. **CSS样式应用**:`<div>`元素本身没有默认样式,可以通过CSS来设置宽度...
每个依赖都包含`<groupId>`, `<artifactId>`, `<version>`,有时还会包含`<scope>`(如`compile`, `test`等),定义了依赖的范围,例如编译时需要还是仅测试时需要。 `<build>`元素是用来配置项目的构建过程的。...
<li>子节点1</li> <li>子节点2 <ul> <li>孙子节点1</li> <li>孙子节点2</li> </ul> </li> </ul> </li> </ul> ``` 在实际应用中,我们通常会结合JavaScript来实现交互功能,如展开/折叠节点、添加或删除...
1. **源码分析**:如何在实际项目中使用`<header>`和`<meta>`标签,以及它们在不同框架(如React、Vue等)中的应用。 2. **调试工具**:开发者可以使用浏览器的开发者工具检查和修改`<header>`和`<meta>`标签,以便...