数组变换问题

1、数组切分问题

题目: 输入一个正序排列的整型数组,如果它可以被切分为1个或多个子序列,输出True,反之False。子序列需为连续的整型数组,并且长度至少为3。
示例:
例1:
输入: [1,2,3,3,4,5]
输出:True
解释:可以切分为2个各自连续的子序列:
1, 2, 3
3, 4, 5
例2:
输入: [1,2,3,3,4,4,5,5]
输出:True
解释:可以切分为2个各自连续的子序列:
1, 2, 3, 4, 5
3, 4, 5
例3:
输入: [1,2,3,4,4,5]
输出:False
解释:无法切分出长度至少为3的子序列。

分析: 此题用贪心算法。创建两个哈希map,freq用来建立数和其出现次数的映射,need记录这个数字可以跟在已有序列后面的次数。首先使用两个哈希map。
freq[i]:存储原数组中数字i出现的次数
need[i]:存储以数字i结尾的且符合题意的连续子序列个数

  • 先去寻找一个长度为3的连续子序列i, i+1, i+2,找到后就将freq[i], freq[i+1], freq[i+2]中对应数字消耗1个(即-1),并将need[i+2]加1,即以i+2结尾的子序列个数+1。
  • 如果后续发现有能够接在这个连续子序列的数字i+3,则延长以i+2为结尾的连续子序列到i+3,此时消耗freq[i+3]一个,由于子序列已延长,因此need[i+2]减1,need[i+3]加1。
    在不满足上面的情况下:
  • 如果freq[i]为0,说明这个数字已经消耗完,可以不管了
  • 如果freq[i]不为0,说明这个数字多出来了,且无法组成连续子序列,所以可以直接返回false了。因此,只有检查到某个数时,这个数未被消耗完,且既不能和前面组成连续子序列,也不能和后面组成连续子序列时,无法分割。
bool solution(vector<int> input){
    if (input.size()<3) return false;
    unordered_map<int, int> freq;
    for (int i:input){
        freq[i]++;
    }
    unordered_map<int, int> seq;
    for(int i:input){
        if(freq[i]==0) continue;
        if(seq[i-1]!=0){
            seq[i-1]--;
            seq[i]++;
            freq[i]--;
        }else if(freq[i+1]>0&&freq[i+2]>0){
            freq[i]--;
            freq[i+1]--;
            freq[i+2]--;
            seq[i+2]++;
        }else{
            return false;
        }
    }
    return true;
}

2、数组极差问题

小Q的好朋友牛牛在纸上写了长度为n的正整数数列。牛牛要求小Q每次从数列中选取两个数a,b,把这两个数从数列中移除出去,然后在数列中加入a * b + 1,直到只剩一个数为止。小Q发现根据操作顺序的不同,最后得到的数的大小也不一样。小Q现在想让你帮他计算,在所有情况中能获得的最大值减去能获得的最小值等于多少? 极差问题证明

输入:
3
1
2
3
输出:
2

解题方法:

第一步:先要对你输入的一个数组进行排序

第二步:擦掉最小的两个数,然后插入a*b+1 继续擦掉最小的数,最终得到最大值。

第三步:擦掉最大的两个数,然后插入a*b+1继续擦掉最大的数,最终得到最小值。

int solution2(vector<int> input){
    int len=input.size();
    if (len<3) return 0;
    sort(input.begin(),input.end());
    int max=input[0]*input[1]+1;
    int min=input[len-1]*input[len-2]+1;
    for (int i=2;i<len;i++){
        max=max*input[i]+1;
    }
    for (int i=len-3;i>=0;i--){
        min=min*input[i]+1;
    }
    return max-min;
}

3、自增数组问题

题目: 小Q发现了一种特殊的数组,叫做自增数组。这个数组支持一种操作:每次操作可以把数组中的一个数自增1.
现在有一个长度为n的自增数组,小Q现在想利用这个操作把数组中的每个数变得不一样,英文最少需要多少次操作?

输入描述:
第一行,一个整数n(n<=10000)
第二行,n个空格间隔的整数,即数组中的元素ai(-10000<=ai<10000)

input
5
1 2 3 2 5
output
2

int solution3(vector<int> input){
    int len=input.size();
    if (len<=1) return 0;
    sort(input.begin(),input.end());
    int maxLen=max(len+1,input[len-1]+1);
    vector<int> dup;
    for (int i=1;i<len;i++){
        if(input[i]==input[i-1])
            dup.push_back(input[i]);
    }
    vector<int>newArray(maxLen+dup.size(),0);
    for (int i=0;i<len;i++){
        newArray[input[i]]=input[i];
    }
    int index=0;
    int cnt=0;
    for (int i=0;i<len;i++){
       if (index==dup.size()) break;
       if(newArray[i]!=i){
            if(dup[index]<i){
              cnt+=(i-dup[index++]);
            } 
        }
        
    }
    return cnt;
}

4、数组排序为最大值

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

示例 1:
输入:nums = [10,2]
输出:“210”

class Solution {
public:

    static bool cmp(int a,int b){
        string sa = to_string(a);
        string sb = to_string(b);
        return sa+sb>sb+sa;
    }

    string largestNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end(),cmp);
        string ret;
        for(auto num:nums){
            if(!(num==0&&ret[0]=='0')) ret+=to_string(num);
        }
        return ret;
    }
};

5、数组位置重排列

给定一个数组int[],对数组元素进行重新排列,要求对于数组中的元素a,如果a%3=0排在前面,a%3=1排中间,a%3=2排后面

{1,3,2,7,4,6,9} -> {3,6,9,1,4,7,2}

void sort(vector<int>array){
    if (array.size()<=1) return ;
    int left=sortFront0(array);
    if(left>=array.size()-1) return;
    sort1and2(left+1,array);
    return;
}
int sortFront0(vector<int>&array){
    if (array.size()<=1) return ;
    int left=0;
    int right=array.size()-1;
    while(left<=right){
        while(array[left]%3==0&&left<=right) 
            left++;
        while(array[right]%3!=0&&left<=right)
            right--;
        swap(array,left,right);
    }
    return left
}
void sort1and2(int start, vector<int>&array){
   int left=start;
   int right=array.size()-1;
   while(start<=end){
       while(array[left]%3==1&&left<=right) 
            left++;
        while(array[right]%3==2&&left<=right)
            right--;
        swap(array,left,right);
   }
   return;
}

6、字符串Z字型

将字符串 “PAYPALISHIRING” 以Z字形排列成给定的行数:
P A H N
A P L S I I G
Y I R

思路: 通过从左向右迭代字符串,我们可以轻松地确定字符位于 Z 字形图案中的哪一行。

  • 我们可以使用 min(numRows,len(s)) 个列表来表示 Z 字形图案中的非空行。

  • 从左到右迭代 s,将每个字符添加到合适的行。可以使用当前行和当前方向这两个变量对合适的行进行跟踪。

  • 只有当我们向上移动到最上面的行或向下移动到最下面的行时,当前方向才会发生改变。

class Solution {
public:
    string convert(string s, int numRows) {

        if (numRows == 1) return s;

        vector<string> rows(min(numRows, int(s.size())));
        int curRow = 0;
        bool goingDown = false;

        for (char c : s) {
            rows[curRow] += c;
            if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
            curRow += goingDown ? 1 : -1;
        }

        string ret;
        for (string row : rows) ret += row;
        return ret;
    }
};

7、两数相除

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2

示例 1:

输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333…) = truncate(3) = 3

class Solution {
public:
//dividend = divisor*2^2 + divisor*2^1 + divisor*2^0
//ans = 2^2 + 2^1 + 2^0
int divide(int dividend, int divisor) {
    long ans = 0, up = std::fabs(dividend), down = std::fabs(divisor);
    while(up >= down){
        long count = 1, base = down;
        while(up > (base << 1)){
            count <<= 1;
            base <<= 1;
        }
        ans += count;
        up -= base;
    }
    ans = ((dividend < 0)^(divisor < 0)) ? -ans : ans;
    return (ans > INT_MAX || ans < INT_MIN) ? INT_MAX : ans;
}
};

8、整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

输入:x = 123
输出:321

class Solution {
public:
    int reverse(int x) {
        int max = 0x7fffffff, min = 0x80000000;//int的最大值最小值
        long rs = 0;//用long类型判断溢出
        for(;x;rs = rs*10+x%10,x/=10);//逆序,正负通吃,不用单独考虑负值
        return rs>max||rs<min?0:rs;//超了最大值低于最小值就返回0    
    }
};

9、最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。

示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”

思路: 第0个字符串依次和第1个,第二个 依次比较。

class Solution {
public:
	string longestCommonPrefix(vector<string>& strs) {
		int length = strs.size();
        if (length==0)
            return "";
		string res;
		int minum = INT_MAX;
		int temp = 0;
		for (int i = 1; i < length; i++) {
			for (int j = 0; j < min(strs[0].size(),strs[i].size()); j++) {
				if (strs[0][j]==strs[i][j])
					temp++;
				else
					break;
			}
			minum = min(minum,temp);
			temp = 0;
		}

		return strs[0].substr(0,minum);
	}
};


10、删除有序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

class Solution {
public:
int removeDuplicates(vector<int>& nums) {
        if (nums.size() < 2) 
            return nums.size();
        int j = 0;
        for (int i = 1; i < nums.size(); i++)
        {	if (nums[j] != nums[i]) 
                nums[++j] = nums[i];
        }
        return ++j;
    }
};

2、给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 不需要考虑数组中超出新长度后面的元素。

class Solution {
public:
	int removeDuplicates(vector<int>& nums) {
		if (nums.size() <= 1)
			return nums.size();
		int pre = nums[0];
        int dup=1;
		for (auto it = nums.begin()+1; it != nums.end();) {
			if (pre == *it)
            {   
              if(++dup>2)
				it = nums.erase(it);
              else
                it++;
            }else{
				pre = *it;
				it++;
                dup=1;
			}
		}
		return nums.size();
	}
};

11、移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int j=0;
        for(int i=0;i<nums.size();i++){
            if(nums[i]!=val)
                nums[j++]=nums[i];
        }
        return j;   
    }
};

12、实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

示例 1:输入:haystack = “hello”, needle = “ll” 输出:2
示例 2:输入:haystack = “aaaaa”, needle = “bba” 输出:-1

class Solution {
public:
    int strStr(string haystack, string needle) {
        int i = 0;
        int j = 0;
        while(haystack[i]!='\0'&&needle[j]!='\0')
        {
            if(needle[j]==haystack[i])//判断是否相等
            {
                j++;
                i++;
            }
            else//不相等退回开始的位置,i+1,j=0;
            {
                i = i - j + 1;
                j = 0;
            }

        }
        if(j == needle.length())//j为步长
       		 return  i-j;
       		 
        return -1;
    }
};

13、字母异形词分组

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        vector<vector<string>> res;
        int length=strs.size();
        if(length==0)
             return res;
        map<string,vector<string>> mstr;
        for(int i=0;i<length;i++){
            string temp=strs[i];
            sort(temp.begin(),temp.end());
            mstr[temp].push_back(strs[i]);
        }
        for(auto it=mstr.begin();it!=mstr.end();it++)
        {
            res.push_back(it->second);
        }
        return res;   
    }
};

14、实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。

class Solution {
public:
    double myPow(double x, int n) {
        long n1=n;
        if(n1==0)
             return 1;
        if(n1==1)
            return x;
        bool daoshu=false;
        if(n1<0)
        {
            n1*=-1;
            daoshu=true;
        }
        if(n1%2==0)
        {    auto temp=myPow(x,n1/2);
             return daoshu? 1/(temp*temp):temp*temp;
        }else{
            auto temp=myPow(x,n1/2);
            return daoshu?1/(temp*temp*x):temp*temp*x;
        }
        return -1;
        
    }
};

15、旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

在这里插入图片描述

class Solution {
public:
	void rotate(vector<vector<int>>& matrix) {
         int len = matrix.size();
        for (int i = 0; i < len / 2; i++) {
            int start = i;
            int end = len - i - 1;
            for (int j = 0; j < end - start; j++) {
                int temp = matrix[start][start + j];
                matrix[start][start + j] = matrix[end - j][start];
                matrix[end - j][start] = matrix[end][end - j];
                matrix[end][end - j] = matrix[start + j][end];
                matrix[start + j][end] = temp;
            }
        } 
    } 
};

16、给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。你可以将其视作是由递归公式定义的数字字符串序列:

countAndSay(1) = “1”; countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    

第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”

class Solution {
public:
	string countAndSay(int n) {
		if (n == 1)
			return "1";
		int i = 2;
		string pre = "1";
		string res = "";
		int dup = 1;
		for (int i = 2; i <= n; i++) {
			res = "";
			for (int j = 0; j < pre.size(); j++) {
				if (j < pre.size() - 1 && pre[j] == pre[j + 1])
					dup++;
				else {
					res += ('0'+dup);
					res+= pre[j];
					dup = 1;
				}
			}
			pre = res;
		}
		return res;
	}
};

17、旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	ListNode* rotateRight(ListNode* head, int k) {
		if (head == nullptr)
			return head;
		int length = 0;
		ListNode* h = head;
		while (h != nullptr)
		{
			length++;
			h = h->next;
		}
		int temp = k % length;//倒数第temp个几点为头结点,前k-n个节点先取出。
		if (temp == 0)
			return head;
		h = head;
		for (int i = 1; i <length - temp; i++) 
			h = h->next;
		ListNode* newhead = h->next;
		h->next = nullptr;
		h = newhead;
		while (h->next!=nullptr) 
			h = h->next;
		h->next = head;
		return newhead;
	}
};

18、螺旋矩阵II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
在这里插入图片描述

class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
        //一层层向内扩展
        vector<vector<int>>  res(n,vector<int>(n,0));
        if(n==0)    return {{}};
        int up=0,down=n-1,left=0,right=n-1;
        int val=1;
        while(left<=right && up<=down){
            for(int i=left;i<=right;++i)    res[up][i]=val++;
            up++;
            for(int i=up;i<=down;++i)    res[i][right]=val++;
            right--;
            for(int i=right;i>=left;--i)    res[down][i]=val++;
            down--;
            for(int i=down;i>=up;--i)    res[i][left]=val++;
            left++;
        }
        return res;
    }
};

19、最后一个单词的长度

给你一个字符串 s,由若干单词组成,单词之间用空格隔开。返回字符串中最后一个单词的长度。如果不存在最后一个单词,请返回 0 。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

示例 1:输入:s = “Hello World” 输出:5

class Solution {
public:
    	int lengthOfLastWord(string s) {
		if (s.size() == 0)
			return 0;
		int length = s.size();
		int nonull = -1;
		int null = -1;
		for (int i = length - 1; i >= 0; i--)
		{
			if (s[i] != ' ')
			{
				nonull = i;
				break;
			}
		}
		if (nonull == -1)
			return 0;
		for (int i = nonull - 1; i >= 0; i--)
		{
			if (s[i] == ' ')
			{
				null = i;
				break;
			}
		}
		if (null == -1)
			return nonull+1;
		return nonull - null;
	}
};

20、数字加1

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1: 输入:digits = [1,2,3] 输出:[1,2,4] 解释:输入数组表示数字 123。

class Solution {
public:
	vector<int> plusOne(vector<int>& digits) {
		bool jinwei = false;
		for (int i=digits.size()-1; i>=0;i--) {
			if (digits[i] < 9)
			{
				digits[i] += 1;
				jinwei = false;
				break;
			}
			else
			{
				digits[i] = 0;
				jinwei = true;
			}	
		}
		if (jinwei) {
			digits.insert(digits.begin(),1);
		}
		return digits;
	}
};

21、二进制求和

给你两个二进制字符串,返回它们的和(用二进制表示)。输入为 非空 字符串且只包含数字 1 和 0。

示例 1:

输入: a = “11”, b = “1”
输出: “100”

class Solution {
public:
    string addBinary(string a, string b) {
        int al = a.size();
        int bl = b.size();
        while(al < bl) //让两个字符串等长,若不等长,在短的字符串前补零,否则之后的操作会超出索引
        {
            a = '0' + a;
            ++ al;
        }
        while(al > bl)
        {
            b = '0' + b;
            ++ bl;
        }
        for(int j = a.size() - 1; j > 0; -- j) //从后到前遍历所有的位数,同位相加
        {
            a[j] = a[j] - '0' + b[j];
            if(a[j] >=  '2') //若大于等于字符‘2’,需要进一
            {
                a[j] = (a[j] - '0') % 2 + '0';
                a[j-1] = a[j-1] + 1;
            }
        }
        a[0] = a[0] - '0' + b[0]; //将ab的第0位相加
        if(a[0] >= '2') //若大于等于2,需要进一
        {
            a[0] = (a[0] - '0') % 2 + '0';
            a = '1' + a;
        }
        return a;
    }
};

22、排列序列

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。首先,对于 n 个数的全排列来说,我们先来看第一个数确定之后,之后的全排列的个数:

以 1 开头的全排列的个数为: (n-1)!
以 2 开头的全排列的个数为: (n-1)!
… …
以 n 开头的全排列的个数为: (n-1)!

当确定第一个数之后,我们对于第二个数来说,他的全排列有: (n-2)!个,向后一次类推即可。

class Solution {
public:
    string getPermutation(int n, int k) {
        string res;
        vector<bool> st(11 , 0);
        for(int i = 0; i < n; i++)
        {
            int fac = 1;
            for(int j = 1; j <= n - 1 - i; j++) fac *= j;

            for(int j = 1; j <= n; j++)
            {
                if(!st[j])
                {
                    if(k > fac) k -= fac;
                    else
                    {
                        st[j] = true;
                        res += j + '0';
                        break;
                    }
                }
            }            
        }
        return res;
    }
};


下一个排列

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

思路:排列的原则是一开始尽量将较大的值往后排,当较大的值在后半序列排完了后,在把它与前面恰好小于它的数交换,然后后边序列全部升序,就能得到它的下一个排序,那这个思路是不是有点像我们上面的解题思路,那么接下来就是如何确定这个需要调整的后半序列,自然到这里我们就会试想着把序列变化过程写出来,然后会按照破坏递增趋势为分界点划分前序列(不用调整的)和后序列(需要调整的)。

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();
        for (int i = n - 1; i >= 1; --i) {
            if (nums[i - 1] < nums[i]) {
                int j = n - 1;
                while (nums[j] <= nums[i - 1]) --j;
                swap(nums[j], nums[i - 1]);
                sort(nums.begin() + i, nums.end());
                return;
            }
        }
        reverse(nums.begin(), nums.end());
    }
};

查找无序数组的中位数

int Partition(int *arr, int start, int end)
{
    int left = start;
    int right = end;
    int key = arr[end];   //选取关键字
    while (left < right)
    {
        while (left < right && arr[left] <= key)  //左边找比key大的值
        {
            ++left;
        }
        while (left < right && arr[right] >= key)  //右边找比key小的值
        {
            --right;
        }
        if (left < right)
        {
            swap(arr[left], arr[right]);  //找到之后交换左右的值
        }
    }
    swap(arr[right], arr[end]);
    return left;
}
//求一个无序数组的中位数
int GetMidNumNoSort1(int *arr,int size)
{
    assert(arr);
    int start = 0;
    int end = size - 1;
    int mid = (size - 1) / 2;
    int div = Partition(arr, start, end);
    while (div != mid)
    {
        if (mid < div)   //左半区间找
            end=div-1;
            div = Partition(arr, start, end);
        else    //左半区间找
            start=div+1;
            div = Partition(arr, start, end);
    }
    return arr[mid];   //找到了
}

最小的K个数

查找无序数组中最小的K个数

void mySwap(vector<int> &num, int i, int j){
    int temp = num[j];
    num[j] = num[i];
    num[i] = temp;
}

int myPartition(vector<int> &num, int low, int high){
    int pivot = num[low];
    while(low < high){
        // 将右边比pivot小的放到左边
        while(low < high && num[high] >= pivot)
            high--;
        mySwap(num, low, high);
        while(low < high && num[low] <= pivot)
            low++;
        mySwap(num, low, high);
    }
    return low;
}

vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
    vector<int> ans;
    int length = input.size();
    if(k > length)
        return ans;
    int low = 0;
    int high = length - 1;
    int pivot = myPartition(input, low, high);
    while(pivot != k - 1){
        // k-1在左边继续找
        if(pivot > k - 1){
            high = pivot - 1;
            pivot = myPartition(input, low, high);
        }
        else{
            low = pivot + 1;
            pivot = myPartition(input, low, high);
        }
    }

    for(int i = 0; i < k; i++)
        ans.push_back(input[i]);
    return ans;
}

数组第K大的数

int Partition(int* arr,int low ,int high)
{
    int temp = arr[low];
    while(low < high)
    {
        while(low < high && arr[high] >= temp)
            high--;
        arr[low] = arr[high];
        while(low < high && arr[low] <= temp)
            low++;
        arr[high] = arr[low];
    }
    arr[low] = temp;//确定参考元素的位置
    return low;
}

int KthElement(int * arr,int low, int high,int n ,int k)
{
    if(arr == nullptr || low >= high || k > n)//边界条件和特殊输入的处理
        return 0;
    int pos = Partition(arr,low,high);
    while(pos != n  - k)
    {
        if(pos > n - k)
        {
            high = pos - 1;
            pos = Partition(arr,low,high);
        }
        if(pos < n - k)
        {
            low = pos + 1;
            pos = Partition(arr,low,high);
        }
    }
    return arr[pos];
 
}

字符串中第一次只出现一次的字符

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        map<char,int>mp;//建立map
        for(int i=0;i<str.size();i++)
            mp[str[i]]++;//遍历str对应出现每一次都+1
        for(int i=0;i<str.size();i++)
        {
            if(mp[str[i]]==1)//按照str的顺序查找当出现等于1的时候返回该字符
            {
                return i;//
            }
        }
        return -1;//没有返回-1
    }
};

以逗号分隔符翻转字符串

输入:hello world,you bless god
输出:world hello,god bless you

先以,为分界位置全部反转一次,再以,和空格为分界位再反转一次就行

void reverse_str(string& str,int left,int right)
{
  while(left <= right)
  {
    char tmp = str[left];
    str[left] = str[right];
    str[right] = tmp;
    left++;
    right--;
  }
}
void str_reverse(string& str)
{
  int begin = 0,start = 0,end = 0,flag = 0;
  for(int i = 0;i<str.length();i++)
  {
    if(str[i] == ' ' || str[i + 1] == '\0')
    {
      if(str[i + 1] == '\0')
        end = i;
      else
        end = i - 1;
      flag = start;
      reverse_str(str,start,end);
      start = i + 1;
    }
    if(str[i] == ',' || str[i + 1] == '\0')
    {
      if(str[i + 1] == '\0')
        end = i;
      else
        end = i - 1;
      if(flag != start)
      {
        reverse_str(str,start,end);
        reverse_str(str,begin,end);
        start = begin = i + 1;
      }
      else
      {
        reverse_str(str,begin,end);
        start = begin = i + 1;
      }
    }
  }
}


左旋转字符串

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。
输入: s = “abcdefg”, k = 2
输出: “cdefgab”

思路:先局部翻转,后整体翻转。举例:abcdefg先局部翻转为bagfedc,后整体翻转为cdefgab。

class Solution {
public:
    string LeftRotateString(string &str, int n)
    {
        int len = str.size();
        // 特殊输入
        if(!str.empty() && n <= len && n >= 0)
        {
            int pFirstStart = 0;
            int pFirstEnd = n - 1;
            int pSecondStart = n;
            int pSecondEnd = len - 1;
            // 翻转字符串的前面n个字符
            reverse(str, pFirstStart, pFirstEnd);
            // 翻转字符串的后面部分
            reverse(str, pSecondStart, pSecondEnd);
            // 翻转整个字符串
            reverse(str, pFirstStart, pSecondEnd);

        }
        return str;
    }
    // 翻转函数
    void reverse(string &str, int begin, int end)
    {
        while(begin < end)
        {
            char tmp = str[begin];
            str[begin] = str[end];
            str[end] = tmp;
            begin++;
            end--;
        }
    }
};


翻转字符串

题目:输入的是一个英文的句子,要求翻转句子中的单词的顺序,但是单词内的字符的顺序却不能变。例如:输入“I am a student.”输出的则是“student a am I”。

还是基 翻转,先全部翻转一次,然后碰到空格即为每个单词,翻转一次。可以自己写函数,也可以用stl的reverse函数

class Solution {
public:
    void reverse(string &str,int a,int b)
    {
        char c;
        while(a<=b)
        {
            c=str[a];
            str[a]=str[b];
            str[b]=c;
            a++,b--;
        }
    }
    string ReverseSentence(string str) {
        if(str.empty())return str;
        int start=0,len=str.length()-1;
        reverse(str,start,len);
        for(int i=0;i<=len;++i)
        {
            if(str[i]==' ')
            {
                reverse(str,start,i-1);
                start=i+1;
            }
            if(i==len)
                reverse(str,start,len);
        }
        return str;
    }
};

找出大于某值的最小的不重复数

题目:n为正整数,求比这个数大且最小的不重复数,重复数为相邻两位数字相同,如1101为重复数,1231为不重复数。

举例:当n = 2222时,输出2301

 
bool IsNoRepetition(int nNum)
{
	int nCurBit = 0;
	int nLastBit = -1;
	while (nNum)
	{	
		if (-1 == nLastBit)
		{
			nLastBit = nNum % 10;
		}
		else
		{
			nCurBit = nNum % 10;
			if (nCurBit == nLastBit)
			{
				return false;
			}
			nLastBit = nCurBit;
		}
		nNum /= 10;
	}
	return true;
}
 
int MaxNum_Force(int nNum)
{
	assert(nNum > 9);
	while (!IsNoRepetition(nNum))
	{
		nNum++;
	}
	return nNum;
}

有序数组中的单一元素

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:
输入: nums = [3,3,7,7,10,11,11]
输出: 10

分析:因为题目限制时间复杂度为 O(log n),所以用二分法,观察二分遍历的条件。从0开始的数组中,数两两相等,则:

  • 在未遇到单一元素时,任意一对数字的下标 nums[x] = nums[x+1] 应满足 x 为偶数,x+1 为奇数。
  • 在遇到单一元素以后,任意一对数字的下标 nums[x] = nums[x+1] 应满足 x 为奇数,x+1 为偶数。
class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int l = 0, r = nums.size() - 1;
        while(r>l)
        {
            int mid=(l+r)>>1;
            if(mid%2==0) //如果mid是偶数
            {
                if(nums[mid]==nums[mid+1]) l=mid+1; //在未遇到单一元素时,左边应该都满足nums[mid]==nums[mid+1],直接向右查询。
                else r=mid; //反之向左查询
            }
            else
            {
                if(nums[mid]==nums[mid-1]) l=mid+1;
                else r=mid;
            }
        }
        return nums[l];
    }
};

除自身以外数组的乘积:

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]

思路: 我们不必将所有数字的乘积除以给定索引处的数字得到相应的答案,而是利用索引左侧所有数字的乘积和右侧所有数字的乘积(即前缀与后缀)相乘得到答案。

  • 初始化两个空数组 L 和 R。对于给定索引 i,L[i] 代表的是 i 左侧所有数字的乘积,R[i] 代表的是 i 右侧所有数字的乘积。
  • 我们需要用两个循环来填充 L 和 R 数组的值。对于数组 L,L[0] 应该是 1,因为第一个元素的左边没有元素。对于其他元素:L[i] = L[i-1] * nums[i-1]。
    同理,对于数组 R,R[length-1] 应为 1。length 指的是输入数组的大小。其他元素:R[i] = R[i+1] * nums[i+1]。
  • 当 R 和 L 数组填充完成,我们只需要在输入数组上迭代,且索引 i 处的值为:L[i] * R[i]。
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int length = nums.size();

        // L 和 R 分别表示左右两侧的乘积列表
        vector<int> L(length, 0), R(length, 0);

        vector<int> answer(length);

        // L[i] 为索引 i 左侧所有元素的乘积
        // 对于索引为 '0' 的元素,因为左侧没有元素,所以 L[0] = 1
        L[0] = 1;
        for (int i = 1; i < length; i++) {
            L[i] = nums[i - 1] * L[i - 1];
        }

        // R[i] 为索引 i 右侧所有元素的乘积
        // 对于索引为 'length-1' 的元素,因为右侧没有元素,所以 R[length-1] = 1
        R[length - 1] = 1;
        for (int i = length - 2; i >= 0; i--) {
            R[i] = nums[i + 1] * R[i + 1];
        }

        // 对于索引 i,除 nums[i] 之外其余各元素的乘积就是左侧所有元素的乘积乘以右侧所有元素的乘积
        for (int i = 0; i < length; i++) {
            answer[i] = L[i] * R[i];
        }

        return answer;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值