60第k个排列(康托);73矩阵置零;74搜索二维矩阵;33搜索旋转排序数组;81搜索旋转排序数组 II

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:


    "123"
    "132"
    "213"
    "231"
    "312"
    "321"


给定 n 和 k,返回第 k 个排列。

说明:


    给定 n 的范围是 [1, 9]。
    给定 k 的范围是[1,  n!]。


示例 1:

输入: n = 3, k = 3
输出: "213"


示例 2:

输入: n = 4, k = 9
输出: "2314"

康托编码

解题思路
    采用康托编码的思路。其实就是康托展开的逆过程。康托展开用来求某个全排列数是第几小的数,也就是当这些数按顺序排时第几个数。
    过程如下:比如求321 是 第几小的,可以这样来想:小于3的数有1和2 两个,首位确定之后后面两位有2!中情况,所以共有2*2!=4种。
    小于2的数只有一个1,所以有11!=1种情况,最后一位是1,没有比一小的数,所以是00!=0
    综上:小于321的数有4+1=5个,所以321是第六小的数。
    逆过程就是已知这个数是第k个数,求这个数是多少,当然是知道n的值的。
    第k个数就是有k-1个数比这个数小。
    所以就是 k-1=an*(n-1)!+an-1*(n-2)!+....+a1*0!;
    再举一个例子:如何找出第16个(按字典序的){1,2,3,4,5}的全排列?
    首先用16-1得到15
    用15去除4! 得到0余15
    用15去除3! 得到2余3
    用3去除2! 得到1余1
    用1去除1! 得到1余0
    有0个数比它小的数是1,所以第一位是1
    有2个数比它小的数是3,但1已经在之前出现过了所以是4
    有1个数比它小的数是2,但1已经在之前出现过了所以是3
    有1个数比它小的数是2,但1,3,4都出现过了所以是5
    最后一个数只能是2
    所以排列为1 4 3 5 2

 

class Solution {
public:
    string getPermutation(int n, int k) {
        string s="123456789",res="";
        if(k==1||n==1)return s.substr(0,n);
        vector<int>nn(n);//n!
        nn[0]=0;//0!
        nn[1]=1;//1!
        --k;
        for(int i=2;i<n;++i)
            nn[i]=i*nn[i-1];
        for(int i=n-1;i>0;--i){
            auto it=s.begin()+k/nn[i];//auto it=s.begin();advance(it,k/nn[i]);
            k=k%nn[i];
            res+=(*it);//res.push_back(*it);
            s.erase(it);//!!!
        }
        res+=s[0];
        return res;
    }
};

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

示例 1:

输入: 
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
输出: 
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]


示例 2:

输入: 
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
输出: 
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]

进阶:


    一个直接的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
    一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
    你能想出一个常数空间的解决方案吗?

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        unordered_set<int>rowCol;//O(m+n)额外空间
        int m=matrix.size();
        if(m==0)return;
        int n=matrix[0].size();
        if(n==0)return;
        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
                if(matrix[i][j]==0)
                    rowCol.insert(i),rowCol.insert(m+j);
        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
                if(rowCol.count(i)||rowCol.count(m+j))
                    matrix[i][j]=0;
    }
};
//matrix[0<=i<m][0]==0表示第i+1行应该置0
//matrix[0][1<=j<n]==0表示第j+1列应该置0;matrix00IsCol==true表示第1列应该置0
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m=matrix.size();
        if(m==0)return;
        int n=matrix[0].size();
        if(n==0)return;
        bool matrix00IsCol=false;//常数额外空间
        for(int i=0;i<m;++i){
            if(matrix[i][0]==0)matrix00IsCol=true;
            for(int j=1;j<n;++j)//j=0出错:[[1,1,1],[0,1,2]]
                if(matrix[i][j]==0)
                    matrix[0][j]=0,matrix[i][0]=0;
        } 
        for(int i=1;i<m;++i)//处理第2列~第n列 第2行~第m行应该置0的情况
            for(int j=1;j<n;++j)  
                if(matrix[i][0]==0||matrix[0][j]==0)              
                    matrix[i][j]=0;
        if(matrix[0][0]==0)//处理第1行应该置0的情况
            for(int j=1;j<n;++j)
                matrix[0][j]=0;
        if(matrix00IsCol)//处理第1列应该置0的情况
            for(int i=0;i<m;++i)
                matrix[i][0]=0;
    }
};

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:


    每行中的整数从左到右按升序排列。
    每行的第一个整数大于前一行的最后一个整数。


示例 1:

输入:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
输出: true


示例 2:

输入:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 13
输出: false

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m=matrix.size();
        if(m==0)return false;
        int n=matrix[0].size();
        if(n==0)return false;
        int le=0,ri=m*n-1;
        while(le<=ri){
            int mid=le+((ri-le)>>1);
            int i=mid/n,j=mid%n;
            if(target==matrix[i][j])return true;
            else if(target<matrix[i][j])ri=mid-1;
            else le=mid+1;
        }
        return false;
    }
}

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4


示例 2:

输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

class Solution {
public:
    int search(vector<int>& nums, int target) {
         int le=0,ri=nums.size()-1;
        while(le<=ri){
            int mid=le+((ri-le)>>1);
            if(nums[mid]==target)return mid;
            if(nums[mid]>=nums[le])//[le~mid]有序
                if(nums[le]<=target&&target<nums[mid])ri=mid-1;//判断是否属于有序部分,即大于等最小值&&小于等于最大值
                else le=mid+1;
            else//[mid~ri]有序
                if(nums[mid]<target&&target<=nums[ri])le=mid+1;//判断是否属于有序部分,即大于等最小值&&小于等于最大值
                else ri=mid-1;
        }
        return -1;
    }

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

示例 1:

输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true


示例 2:

输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false

进阶:


    这是 搜索旋转排序数组 的延伸题目,本题中的 nums  可能包含重复元素。
    这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int le=0,ri=nums.size()-1;
        while(le<=ri){
            int mid=le+((ri-le)>>1);
            if(nums[mid]==target)return true;

            //nums[mid]==nums[le]的情况判断不出是[le-mid]还是[mid-ri]有序,所以要特殊处理,即+1直到不相等的情况出现为止
            while(le<=ri&&nums[mid]==nums[le])++le;
            if(le>mid)continue;

            if(nums[mid]>nums[le])//[le-mid]有序
                if(nums[le]<=target&&target<nums[mid])ri=mid-1;
                else le=mid+1;
            else//[mid-ri]有序
                if(nums[mid]<target&&target<=nums[ri])le=mid+1;
                else ri=mid-1;
        }
        return false;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值