剑指Offer C++ --- 数组篇2

本文介绍了几种编程问题的解决方案,包括在矩阵中查找特定单词路径的回溯算法,打印n位数,调整数组奇偶顺序,顺时针打印矩阵,验证栈的压入弹出序列,找数组中出现次数超过一半的数字,获取最小k个数以及使用动态规划解决硬币组合和斐波那契数列问题。这些题目涉及多种算法和数据结构的应用。
摘要由CSDN通过智能技术生成

12.矩阵中的路径

思路:

从矩阵第一个元素开始判断,它是不是word的第一个字符,如果不是,继续判断下一个

如果是,则判断其上,下,左,右只要有一个满足,其是word的第一个字符,继续判断下一个。。。 如果不是,则回到矩阵的位置,继续判断下一个是不是word的开始字符

很明显,要用回溯算法,进行深度遍历

int row;
int col;

bool exist(vector<vector<char>>&board, string word)
{
    row = board.size();
    col = board[0].size();
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            if(dfs(board,word,i,j,0))
            {
                return true;
            }
        }
       
    }
    return false;
}



bool dfs(vector<vector<char>>&board,string& word,int i,int j,int k)
{
    //矩阵中的字母不合适退出
    if(i<0 || i>=row || j<0 || j>=col || board[i][j] != word[k])
    {
        return false;
    }

    //完全匹配成功
    if(word.size()-1 == k)
    {
        return true;
    }

    //能走到这儿,说明board[i][j]的字母与word的字母匹配成功
    //board[i][j]置为'\0'
    board[i][j] = '\0';
    
    //上 下 左  右进行深度探索
    bool res = dfs = (board,word,i+1,j,k+1) || (board,word,i-1,j,k+1) 
                     || (board,word,i,j-1,k+1) || (board,word,i,j+1,k+1);
    
    //回溯过程中,将矩阵中修改的元素重新填回
    board[i][j] = word[k];

    return res;
}

13.打印从1到最大的n位数

vector<int> printNumbers(int n)
{
   int max = pow(10,n) - 1;
   vector<int> nums;
   nums.reserve(max);
   
   for(int i = 1; i <= max; ++i)
   {
       nums.emplace_back(i);
   }

   return nums;
} 

14.调整数组顺序使奇数位于偶数之前

思路:首尾开工

    vector<int> exchange(vector<int>& nums) 
    {
        int i = 0;
        int j = nums.size()-1;
        
        while(i<j)
        {
            //遇到奇数直接跳过
            while(i<j && (nums[i] & 1) == 1)
            {
                ++i;
            }
          
            //遇到偶数直接跳过
            while(i<j && (nums[j] & 1) == 0)
            {
                --j;
            }
            
            //交换
            if(i < j)
            {
               int tmp = nums[i];
               nums[i] = nums[j];
               nums[j] = tmp;
               ++i;
               --j;
            }
        }

        return nums;
    }

15.顺时针打印矩阵

思路:

转化四种状态:

left  ---》 right

top  ---》 bottom

right  ---》left

bottom  ---》 top

退出条件:左边界 > 右边界,上边界 > 下边界

每遍历一次就需要检查一次是否越界

vector<int> spiralOrder(vector<vector<int>>& matrix)
{
    //控制列
    int left = 0; //左边界
    int right = matrix[0].size()-1; //右边界
    
    //控制行
    int top = 0;  //上边界
    int bottom = matrix.size()-1;  //下边界
    
    vector<int> vec;
    vec.reserve((right+1)*(bottom+1));
    
    while(true)
    {
      //left -> right
      for(int i = left;i<=right;++i)
      {
         vec.emplace_back(matrix[top][i]);
      }
      
      //检查边界,并跳到下一行
      if(++top > bottom)  break;
      
      //top -> bottom
      for(int i = top;i<=bottom;++i)
      {
          vec.emplace_back(matrix[i][right]);
      }
      
      //检查边界,并跳到下一列
      if(--right < left)  break;
      
      //right -> left
      for(int i = right;i>=left;--i)
      {
          vec.emplace_back(matrix[bottom][i]);
      }

      //检查边界,并跳到上一行
      if(--bottom < top)  break;

      //bottom -> top
      for(int i = bottom;i>=top;--i)
      {
          vec.emplace_back(matrix[left][i]);
      }
       
      //检查边界,并跳到下一列
      if(++left > right)  break;
    }

    return vec;

}

16.栈的压入、弹出序列

思路:

借助栈来判断,每向栈内入一个元素,循环判断栈顶元素是否与出栈序列的元素相等,如果相等则出栈,序列向后迭代一个,继续比较。。。

如果栈为空,则出栈序列正确,否则错误

bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
    statct<int> st;
    int i = 0;

    for(auto x:pushed)
    {
       st.push(x);    
       while(!st.empty() && st.top() == popped[i])
       {
            st.pop();
            i++;
       }
    }
    return st.empty();
}

17.数组出现次数超过一半的数字

思路:假设众数val = x,num = 1,遍历后续元素,如果相等则num+=1,如果不相等,则num -=1

当num = 0时,下一个遍历的元素即就是x,num重新赋值为1

int majorityElement(vector<int>& nums)
{
    int x = 0;
    int n = 0;

    for(auto val : nums)
    {
        if(n == 0)
        {
           x = val;
           n = 1;
        }
        else
        {           
           x != val ? n -= 1 : n += 1;
        }
    }
    return x;
} 

18.最小的k个数

思路:利用优先级队列构建小根堆,然后取堆顶前k个元素

vector<int> getLeastNumbers(vector<int>& arr, int k)
{
    if(k == 0)
    {
       return vector<int>();
    }

    //构建小根堆
    priority_queue<int,vector<int>,greater<int>> q;
    for(auto x : arr)
    {
       q.push(x);
    }

    vector<int> vec;
    vec.reserve(k);
    
    while(k-- != 0)
    {
       vec.emplace_back(q.top));
       q.pop();  
    }
    return vec;
} 

19.有硬币1,3,5若干,求组成金额为11的最小的硬币数

//分治
int func(int n)
{
    if(n == 1 || n == 3 || n == 5)
    {
        return 1;
    }
    
    else if(n == 2 || n == 4)
    {
        return 2;
    }

    else
    {
        int n1 = func(n-1)+1;
        int n2 = func(n-3)+1;
        int n3 = func(n-5)+1;
        return min({n1,n2,n3});
    }
}

//动态规划
void fun()
{
    int v[] = {1,3,5}; //硬币的面额组成
    int length = sizeof(v)/sizeof(v[0]);
    int c = 11;  //面值11

    int dp[12]{}; //dp数组,存放最优子结构
    
    for(int i = 1; i <= c; ++i)
    {
        dp[i] = i;    //表示初始全部由1分硬币组成
        for(int j = 0; j < length; ++j)
        {
           if(i >= v[j] && (1 + dp[i-v[j]]< dp[i]]))
           dp[i] = 1+dp[i-v[j]];
        }
    }
         
    cout << dp[c] << endl;
    return 0;
}

动态规划的核心思想:将子问题的最优子解存下来,在求取后续子问题时,可以进行复用

20. 斐波那契数列

a.分治

int fib(int n) 
{
    if(n == 0)
    {
       return 0;
    }
    else if(n == 1)
    {
       return 1;
    }
    else
    {
       return fib(n-2)+fib(n-1);
    }
}

b.动态规划(递归)

    int fib(int n)
    {
       vector<int> dp(n+1,-1);
       return fib(dp,n);
    }

    int fib(vector<int>& dp,int n)
    {
        if(dp[n] != -1)
        {
            return dp[n];
        }

        if(n == 0)
        {
            dp[n] = 0;
            return dp[n];
        }
        else if(n == 1)
        {
            dp[n] = 1;
            return dp[n];
        }
        else
        {
            dp[n] = fib(n-2) + fib(n-1);
            return dp[n];
        }
        return dp[n];
    }

c.动态规划(非递归)

    int fib(int n) 
    {
       //状态数组
       vector<int> dp(n+2,-1);
       //dp[0] = 0, dp[1] = 1, dp[i] = dp[i-2] + dp[i-1] (i>2)
       dp[0] = 0;
       dp[1] = 1;

       if(n == 0 || n == 1)
       {
           return dp[n];
       }

       for(int i = 2;i<=n;++i)
       {
          dp[i] = dp[i-2]+dp[i-1];
       }
       return dp[n];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值