常用算法的总结
分治
在排序算法中,快速排序和归并排血都用到了分治的思想,所谓分治就是分而治之,再利用递归,将一份比较大的任务分成若干个小任务去完成。
快速排序
快速排序,采用了挖坑填坑的方法来完成每一趟排序,每一次小排序后都会分成左边一组和右边一组,分别小于和大于一个分界数(一般是数组第一个数字)再分别对这两组进行排序。相当于对左右两组分而置之,直到完成排序。
adjustarray(每一趟排序的函数)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| private static int adjustarray(int[] array,int l,int r) {
int i=l;//l和r分别是数组的左指针和右指针
int j=r;//将其赋给i,j,进行指针的移动
while(i<j){
while(i<j && array[j]>=array[l]){
j--;
}
if(i<j){
array[i]=array[j];
i++;
}//先是右指针向左移动找到比临界值小的数,填入上一个坑中
while(i<j && array[i]<=array[l]){
i++;
}
if(i<j){
array[j]=array[i];
j--;
}//接着左指针向右移动找到比临界值大的数,填入上一个坑中
}
array[j]=array[l];//最后把临界值的数填入i=j也就是临街位置的坑中,该值不会再改变
return i;
}
|
接下来是quick_sort的主调用函数
quick_sort1
2
3
4
5
6
7
8
9
10
11
12
| prviate static void quick_sort(int[] array,int l,int r) {
if(l<r){
int mid = adjustarray(array,l,r);
for(int i=0;i<r;i++){
System.out.print(array[i]+" ");
}
System.out.println();
quick_sort(array,l,mid-1);
quick_sort(array,mid+1,r);
}
//这个函数就是分治思想的函数,思想明白了这个函数也就没有什么不能理解的了
}
|
归并排序
归并排序是先分再和的思想,其重点和难点是在和(merge)的函数上,对一趟数组,先将其按中间数字的数值等分为左右两部分,比其小的在左,大的在右(从小到大的顺序),直到其为一个数字,相对有序之后,接着进行合并,将数组两两形成有序数组。
merge操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| private static void merge(int[] array,int l,int mid,int r){
int temp[] = new int[r-l+1];//创建一个临时数组用来承接两个数组合并后的结果
int i=l;
int j=mid+1;
while(i<=mid && j<=r){//比较左右两个数组元素大小,按大小顺序重新排序在临时数组中
if(array[i]<array[j]){
temp[k++] = array[i++];
}
else{
temp[k++] = array[j++];
}
}
while(i<=mid){
temp[k++] = array[i++];
}
while(j<=r){
temp[k++] = array[j++];
}
for(int k1 = 0 ; k1<temp.length;k1++){//将数组还给ararry
array[k1+l] = temp[k1];
}
}
|
接下来是主调用函数 依然是采用了分治的思想
merge_sort1
2
3
4
5
6
7
8
| private static void merge_sort(int[] array, int l, int r) {
int mid = (l+r)/2;
if(l<r){//分治思想
merge_sort(array,l,mid);//将左边有序(直到一个元素)
merge_sort(array,mid+1,r);//使右边有序(直到一个元素)
merge(array,l,mid,r);//合并
}
}
|
动态规划
跳石板问题 && 背包问题
跳石板问题:
小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3…….
这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达。
例如:
N = 4,M = 24:
4->6->8->12->18->24
于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板
输入描述:
输入为一行,有两个整数N,M,以空格隔开。
(4 ≤ N ≤ 100000)
(N ≤ M ≤ 100000)
输出描述:
输出小易最少需要跳跃的步数,如果不能到达输出-1
输入例子:
4 24
输出例子:
5
这道题可以采用动态规划的思想,规划的量是从起点到每一个点的最小的距离。因为每一个点往前前进的步数只能是他的约数,那么到下一个点为这个点加上他的一个约数,所以从起点到这个点的最短距离就是取 上个点的最小距离+1 或者是到这个点的最小距离 中的最小值。
每个点的最短距离设为dp[i] x为i的约数 那么 dp【i+x】 = Math.min(dp[i+x],dp[i]+1)
跳石板问题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
int from = scanner.nextInt();
int to = scanner.nextInt();
int result = calresult(from,to);
System.out.println(result);
}
}
private static int calresult(int from, int to) {
if(from == to){
return 0;
}
int[] dp = new int[to-from+1]; //将规划的量存为dp数组内
int step = to-from+1;
dp[0] = 0;
for(int i=1;i<step;i++){
dp[i] = Integer.MAX_VALUE;
}
for(int i=0;i<step;i++){
if(dp[i] == Integer.MAX_VALUE){
dp[i]=0;
continue;
}
ArrayList<Integer> list = getyinshu(i + from);//求因数的函数
for (int j = 0; j < list.size(); j++) {
int x = list.get(j);
if(i+from+x<=to){
dp[i+x] = Math.min(dp[i+x], dp[i]+1);//整个算法的关键,将这个语句弄懂这个算法显得十分简单
}
}
}
if(dp[to-from] == 0){
return -1;
}else{
return dp[to-from];
}
}
private static ArrayList<Integer> getyinshu(int num) {//求因数的函数
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i=2;i<=Math.sqrt(num);i++){
if(num%i==0){
list.add(i);
if(num/i!=i){//注意这里在写程序的时候出错了,排除其平方数的情况
list.add(num/i);
}
}
}
return list;
}
|
背包问题
题目描述:
有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?