careercup-递归和动态规划 9.3

9.3 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i]=i。给定一个有序整数数组,元素值给不相同,编写一个方法,在数组A中找出一个魔术索引,若存在的话。

进阶:

如果数组元素有重复值,又该如何处理。?

解法一,选择蛮力法,我们可以直接迭代访问整个数组,找出符号条件的元素。

int magicSlow(int arr[],int n)
{
    if(n==0)
        return -1;
    int i;
    for(i=0;i<n;i++)
    {
        if(arr[i]==i)
            return i;
    }
    return -1;
}

解法二:既然给的数组是有序的,我们理应充分利用这个条件。

你看你会发现这个问题与经典的二分查找问题非常相似。充分运用匹配法,就能找到适当的算法,我们又该怎样运用二分查找法呢?

在二分查找中,要找出元素k,我们会先拿它跟数组中间的元素x比较,确定k位于x的左边还是右边。

以此为基础,是否通过检查中间元素就能确定魔术索引的位置?

通过比较A[mid]<mid,可以确定该魔术索引一定在mid的右边,因此left=mid+1;

如果A[mid]>mid,可以取得该魔术索引一定在mid 的左边,因此right=mid-1;

算法如下:

int magicQuick(int arr[],int n)
{
    if(n==0)
        return -1;
    int mid;
    int left=0;
    int right=n-1;
    while(left<=right)
    {
        mid=(left+right)/2;
        if(mid==arr[mid])
            return mid;
        else if(mid<arr[mid])
            right=mid-1;
        else
            left=mid+1;
    }
    return -1;
}

进阶:

如果存在重复元素,前面的算法就会失效。以下面的数组为例:

-10 -5 2 2 2 3 4 7 9 12 13

0   1  2 3 4 5 6 7 8 9 10 11

看到A[mid]<mid时,我们无法判定魔术索引位于数组哪一边。它可能在数组右侧,跟前面一样。或者,也可能在左侧(在本例中的确在左侧)。

它有没有可能在左侧的任意位置呢?不可能。由A[5]=3可知,A[4]不可能是魔术索引。A[4]必须等于4,其索引才能成为魔术索引,但数组是有序的,故A[4]必定小于A[5]。

事实上,看到A[5]=3时按照前面的做法,我们需要递归搜索右半部分。不过,如搜索左半部分,我们可以跳过一些元素,值递归搜索A[0]到A[3]的元素。A[3]是第一个可能成为魔术索引的元素。

综上:我们得到一种搜索模式,先比较midIndex和midValue是否相同。然后,若两者不同,则按如下方式递归搜索左半部分和右半部分。

左半部分:搜索索引从start到min(midIndex-1,midValue)的元素。

右半部分:搜索索引从max(midIndex+1,minValue)到end的元素。

可以看做前面的基础上求左右两边的交集,例如A[mid]<mid,需要搜索mid左边的部分元素,即区间从start到min(midIndex-1,midValue)的元素和mid右边的全部元素。

A[mid]>mid,需要搜索mid左边的全部元素和右边的部分元素,即区间从max(midIndex+1,minValue)到end,然后两者求交集,就得到上面的区间了。

算法如下:

int magicHelper(int arr[],int n,int l,int r)
{
    if(l>r||l<0||r>=n)
        return -1;
    int mid=(l+r)/2;
    if(mid==arr[mid])
        return mid;
    int rightindex=min(mid-1,arr[mid]);
    int left=magicHelper(arr,n,l,rightindex);
    if(left>=0)
        return left;
    int leftindex=max(mid+1,arr[mid]);
    int right=magicHelper(arr,n,leftindex,r);
    return right;
}
//有重复元素
int magicFast(int arr[],int n)
{
    if(n==0)
        return -1;
    return magicHelper(arr,n,0,n-1);
}

完整代码:

#include<iostream>
using namespace std;

int magicSlow(int arr[],int n)
{
    if(n==0)
        return -1;
    int i;
    for(i=0;i<n;i++)
    {
        if(arr[i]==i)
            return i;
    }
    return -1;
}

int magicQuick(int arr[],int n)
{
    if(n==0)
        return -1;
    int mid;
    int left=0;
    int right=n-1;
    while(left<=right)
    {
        mid=(left+right)/2;
        if(mid==arr[mid])
            return mid;
        else if(mid<arr[mid])
            right=mid-1;
        else
            left=mid+1;
    }
    return -1;
}

int magicHelper(int arr[],int n,int l,int r)
{
    if(l>r||l<0||r>=n)
        return -1;
    int mid=(l+r)/2;
    if(mid==arr[mid])
        return mid;
    int rightindex=min(mid-1,arr[mid]);
    int left=magicHelper(arr,n,l,rightindex);
    if(left>=0)
        return left;
    int leftindex=max(mid+1,arr[mid]);
    int right=magicHelper(arr,n,leftindex,r);
    return right;
}
//有重复元素
int magicFast(int arr[],int n)
{
    if(n==0)
        return -1;
    return magicHelper(arr,n,0,n-1);
}

int main()
{
    int arr[10]={-1,0,1,2,3,4,5,6,7,9};
    cout<<magicSlow(arr,10)<<endl;
    cout<<magicQuick(arr,10)<<endl;
    cout<<magicFast(arr,10)<<endl;
}
时间: 2024-10-14 04:31:11

careercup-递归和动态规划 9.3的相关文章

70. Climbing Stairs【leetcode】递归,动态规划,java,算法

You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Note: Given n will be a positive integer. 题目分析:每次只能走1或2步,问n步的话有多少中走法???? 可以用动态规划和递归解

斐波那契数列的实现(简单递归和动态规划)

斐波那契数列的实现(简单递归和动态规划) 一.简单递归的实现 1 #include "stdafx.h" 2 #include <string> 3 using namespace std; 4 int f(int n) 5 { 6 if (n == 0) 7 { 8 return 0; 9 } 10 if (n == 1) 11 { 12 return 1; 13 } 14 return f(n - 1) + f(n - 2); 15 } 16 int _tmain(in

利用递归和动态规划来求解组合数

组合数定义:从m个不同元素中,任取n(n≤m)个元素并成一组,叫做从m个不同元素中取出n个元素的一个组合:从m个不同元素中取出n(n≤m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数. 下面是一种比较通俗的计算公式: 其递归公式为: c(n,m)=c(n-1,m-1)+c(n-1,m) 下面是c++实现该递归算法: #include <iostream> #include <stdlib.h> #define EXIT -1 using namespace st

算法初级面试题08——递归和动态规划的精髓、阶乘、汉诺塔、子序列和全排列、母牛问题、逆序栈、最小的路径和、数组累加成指定整数、背包问题

第八课主要介绍递归和动态规划 介绍递归和动态规划 暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 图灵引入的是:我不知道怎么算,但是我知道怎么试.知道怎么暴力破解出来. 要学会,练习懂得怎么尝试.

左神算法第八节课:介绍递归和动态规划(汉诺塔问题;打印字符串的全部子序列含空;打印字符串的全排列,无重复排列;母牛数量;递归栈;数组的最小路径和;数组累加和问题,一定条件下最大值问题(01背包))

暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 一:递归 1. 汉诺塔问题 汉诺塔问题(不能大压小,只能小压大),打印n层汉诺塔从最左边移动到最右边的全部过程. 左中右另称为 from.to.hel

【动态规划专题】1:斐波拉契数列问题的递归和动态规划

<程序员代码面试指南--IT名企算法与数据结构题目最优解> 左程云 著 斐波拉契数列问题的递归和动态规划 [题目]:给定整数N,返回斐波拉契数列的第N项.补充问题1:给定整数N,代表台阶数,一次可以跨2个或者1个台阶,返回有多少种走法.补充问题2:假设农场中成熟的母牛每年只会生产1头小母牛,并且永远不会死.第一年农场只有1只成熟的母牛,从第2年开始,母牛开始生产小母牛.每只小母牛3年后成熟又可以生产小母牛.给定整数N,求出N年后牛的数量. [举例]斐波拉契数列f(0)=0, f(1)=1,f(

背包问题---递归及动态规划

一.原题 如果有一组物品,各个物品的质量已知,现有一个背包,背包可以容纳的质量总和S已知,问是否能从这N个物品中取出若干个恰好装入这个背包中. 二.递归算法 本质思想:设法尝试全部组合,当部分组合已经无法满足条件时,马上停止当前组合的尝试:若出现第一个满足条件的组合,马上停止尝试.使用递归回溯法实现.(感觉这东西不是我这样的菜鸟可以说明确的,还得自己慢慢体会,最好的方法就是耐住性子跟踪调试). 上"酸菜" #include <stdio.h> #include <st

java——递归(动态规划,回溯)

最近刷面试题经常刷到递归方面的算法,一直以为都是递归,后来发现竟然都有具体的叫法,所以写了这篇博客来牢记以下 1. 五大常用算法 (1) 分治算法 把一个复杂的问题分成两个或多个相同或者相似的子问题,然后不断地细分,直到最后的子问题可以很简单地求解出来,原问题的解就是自问题的合并.比如常见的快速排序算法和归并算法 分治法的核心思想就是把大的难解的问题不断分割,分而治之. (2) 动态规划 类似于分治法,将带求解的问题分化成几个小问题,每个小问题的解会影响原问题的解. 先求每个子问题的局部解,然后

9.9递归和动态规划(一)——小孩上楼梯的方式的种类

/** * 功能:有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶.2阶或3阶.计算小孩上楼梯的方式有多少种. */ 两种方法: 方法一: //递归法 /** * 思路:自上而下的方式. * 最后一步可能是从第n-1阶往上走1阶.从第n-2阶往上走2阶或从第n-3阶往上走3阶. * 因此,抵达最后一阶的走法,抵达这最后三阶的方式的综合. * @param n * @return */ public static int countWays(int n){ if(n<0) return 0;

爬楼问题—递归、动态规划

问题:假设你正在爬楼梯,需要n步你才能到达顶部.但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部? code: 1 //动态规划解决爬楼问题 2 int dp_climbStairs(int n) { 3 if (n == 1) { 4 return 1; 5 } else if (n == 2) { 6 return 2; 7 } else { 8 int * M = new int[n]; 9 for (int i = 0; i < n; i++) { 10 M[i] = 0;