leetcode 中等题(部分一)

1、随机数
#include
srand((int)time(0));
rand()%x;

2、数值题
A:Single Number 类:
题型1)给一个数组,里面有两个元素只出现了一次,而其他所有元素都出现了两次,找到这两个元素,这个主要用位的方法:

    vector singleNumber(vector& nums) {
        // 首先所有的元素进行异或,将会得到这两个数的异或值
        int num=nums[0];
        for(int i=1;i{left,right};
    }

B:Count Numbers with Unique Digits :
Given a non-negative integer n, count all numbers with uniques digits, x, where 0 ≤ x < 10^n.
这个题说的是给你一个n,指代n位的整数,求整数中,所有数字只出现一次的整数的数目。
我在想,这个地方是不是应该反过来查:

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {   
        if(n == 0) return 1;
        int count = 9, ret = 9;
        // In each iteration, try adding 0 - 9 to the end of number
        for(int i = 2; i <= n; ++i) {
            // Advoid repeating digits. f(k) = f(k - 1) * (10 - k - 1)
            count *= 11 - i;
            ret += count;
        }
        // Add 0
        return ret + 1;
    }
};

C:Integer Break
(如果可以,还是有时间补补数学原理)
这个题是说给一个整数n,然后将它分裂成至少N个正整数(N>2),然后将这N个正整数乘起来,返回正整数积最大的值。
这里,数学原因我不知道,但是我只知道最多只能有2个2,也就是说5以下,尽可能考虑2,;5以上,尽可能考虑3,如果余数为1,变为2*2;

class Solution {
public:
    int integerBreak(int n) {
        // 这里,数学原因我不知道,但是我只知道最多只能有2个2,也就是说5以下,尽可能考虑2,;5以上,尽可能考虑3,如果余数为1,变为2*2;
        if(n==1||n==2) return 1;
        if(n<5) return 2*(n-2);
        else{
            if(n%3==1){
                int res = 4;
                res = res * pow(3,(n-4)/3);
                return res;
            }else if(n%3==2){
                return 2*pow(3,(n-2)/3);
            }else{
                return pow(3,n/3);
            }
        }
    }
};

D:Divide Two Integers
(在不用乘、除和取模操作的情况下,进行除法,如果溢出,返回INT_MAX)
(可以用加减和左移右移运算符)
首先考虑单例的情况,divisor 为 0 的时候,返回 INT_MAX,而 dividend 为 0 的时候,返回 0,而 divisor 为 1 的时候,返回 dividend。
【abs() 主要用于对求整数的绝对值,在 "stdlib.h" 头文件里】
【fabs() 主要是求精度要求更高的 double,float 型的绝对值,在 头文件里】
【exp 主要就是计算 e 的多少次方】
【默认 log() 是取自然对数为底 即 ln】
【直接用换底公式 写个简单的表达式就完成了 例如: log2 x 写成 log(x)/log(2)】

 double x1 = log(fabs(dividend));
 double x2 = log(fabs(divisor));
 long long res = double(exp(x1-x2));
class Solution {
public:
    int divide(int dividend, int divisor) {
        if(dividend==0) return 0;
        else if (divisor==0&& dividend>0) return INT_MAX;
        else if (divisor==0&& dividend<0) return INT_MIN;
        else if (divisor==1) return dividend;
        
        double x1 = log(fabs(dividend));
        double x2 = log(fabs(divisor));
        double res = exp(x1-x2);
        
        bool ne = (dividend>0)^(divisor>0);
        if(ne==false){
            if(resINT_MIN) return -res;
            else return INT_MIN;
        } 
        return 0;
    }
};

E:Fraction to Recurring Decimal
[ 给两个数,分别代表分子和分母,然后,以字符串的形式返回分数的小数形式 ]
[ 如果分数部分是循环的,用括号的形式将重复的部分关起来 ]
举例:

Given numerator = 1, denominator = 2, return "0.5".
Given numerator = 2, denominator = 1, return "2".
Given numerator = 2, denominator = 3, return "0.(6)".
(我原来的方法是把小数部分转换为字符串保存下来,然后再进行查找,重点在于这个查找方式)

class Solution {
public:
    string fractionToDecimal(int numerator, int denominator) {
    // 分别判断分子分母是否为0,
        if(numerator==0) return "0";   // 分子为0,那么直接返回 0
        if(denominator==0) return "";     // 分母为0,那么直接返回 ""
        
        string res="";
        unordered_map map;
        if((numerator>0)^(denominator>0)) res+='-';    // 根据正负进行符号判别
        long num1 = abs((long)numerator/(long)denominator);
        res= res+ to_string(num1);
        long num2 = abs((long)numerator%(long)denominator);
        if(num2==0) return res;  // 如果没有小数,那么就直接返回。
        res = res +".";         // 在有小数的情况下,加上点。
        // ---------------------------------------------------------------
        while(num2){        // 这里对应的是余数
            map[num2] = res.size();
            num2=num2*10;
            num1 = abs((long)num2 / (long)denominator);
            res= res+ to_string(num1);
            num2 = abs((long)num2 % (long)denominator);
            if(map.find(num2)!=map.end()){
                res.insert(map[num2],"(");
                res = res+")";
                break;
            }
        }
        return res;
    }
};

F:Maximal Square

// ------------------------用状态矩阵-------------------------
class Solution {
public:
    int maximalSquare(vector>& matrix) {
        if(matrix.size()==0) return 0;

        int row_num = matrix.size();
        int col_num = matrix[0].size();
        
        int max_value=0;
 // 构建一个状态矩阵
        vector record(col_num,0);

        // 初始化状态矩阵
        for(int i=0;i
// 用真正的矩阵
class Solution {
public:
    int maximalSquare(vector>& matrix) {
        int row_num = matrix.size();
        if(row_num==0) return 0;
        int col_num = matrix[0].size();
        
        int sum=0;
        for(int i=0;inum?sum:num;
        }
        
        for(int j=0;jnum?sum:num;
        } 

        for(int i=1;inum?sum:num;
                } 
            }
        return sum*sum;
    }
};

3、链表类题
A:Linked List Random Node(随机数题目)
Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen.
Follow up:
What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without using extra space?

B:Reverse Linked List ii(链表逆转)
(Reverse a linked list from position m to n. Do it in-place and in one-pass)
For example:
Given 1->2->3->4->5->NULL, m=2 and n=4
return 1->4->3->2->5->NULL.

    ListNode* reverseBetween(ListNode* head, int m, int n) {
        int count=0;
        ListNode* left = new ListNode(-1); //我还是需要养成一个好习惯,建立一个新的节点
        left->next = head;
        for(;countnext;
        ListNode* cur = left->next, * back_up = left->next;
  // left 为逆转链表前的那个节点,back_up 为逆转链表的最后一个节点。
  // 这个是链表的逆转,逆转完了后,cur 为逆转完毕后的链表的下一个节点,before 为逆转之后的头节点。
        ListNode* next=nullptr,*before=nullptr;
        for(;countnext;
            cur->next = before;
            before=cur;
            cur=next;
        }
        left->next = before; back_up->next = cur;
        return m==1?before:head;
    }

C:Remove Duplicates from Sorted List ii
(给一个已排序的链表,删除所有有重复数字的节点,留下只出现过一次的数字的节点)

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        // 如果一旦需要删除掉一个链表的节点,必须重新设定节点。
        if(!head) return nullptr; // 节点不存在
        // 声明一个头节点,并设头节点的指针。
        ListNode begin(1);
        ListNode* start = &begin;
        // 进入循环体。
        while(1){
            if(head&&head->next&&head->val==head->next->val){
                while(head&&head->next&&head->val==head->next->val) head = head->next;
                if(!head->next) return begin.next;
                else head=head->next;
            }else if(!head->next){   // 只有一个节点
                start->next = head;
                start=start->next;
                start->next =nullptr;
                return begin.next;  
            }else if(head->next){    // 有连续两个节点,但是两个节点的值不相等。
                start->next = head;
                start= start->next;
                head = head->next;
                start->next =nullptr;
            }
        }
        return begin.next;
    }
};

4、数组题
A、Product of Array Except Self
(这个轮子比较重要,因为,直接抽象为入口是一个数组,返回的数组中每一个元素是其他元素的乘积,但是不能用除法)
For example, given [1,2,3,4], return [24,12,8,6].
虽然要求是用常数的空间复杂度,其实这里也不会那么苛刻,可以用一个满足返回值需求的数组

vector productExceptSelf(vector& nums){
        int size = nums.size();
        vector vec(size,1);
        // 每个数左边的乘积都有了。
        for(int i=1;i=0;i--){
              vec[i] = vec[i]*right;
              right=right*nums[i];
        }
        return vec;
}

Shuffle an Array
[ 将数组进行漂移,返回数组任何一个可能的排列,这个叫做数组的漂移 ]
( 可见,现在的一个出题的趋势,就是走随机数,通过一定程度上的模拟随机)
STL中的函数random_shuffle()用来对一个元素序列进行重新排序(随机的)

ps:这个函数不仅可以用于 vector,还可以用于 数组:

template  
   void random_shuffle(  
      RandomAccessIterator _First, //指向序列首元素的迭代器  
      RandomAccessIterator _Last  //指向序列最后一个元素的下一个位置的迭代器  
   );  

C、对数组进行全排列【特别重要】
(这个部分非常重要,因为全排列的方式有很多种,需要找到最有效率的方式)
注意1:在进行交换之前,先进行全排列。这样方便过滤掉重复的数字。
注意2:将结果集放在最外面,方便随时调用。
注意3:设置数组的交换标志位。

class Solution {
private:
    vector> res; //  将结果集放在最外面,方便随时调用
public:
    void help(vector& nums,int left){  // 在形参中设置交换的标志位
        if(left==nums.size()){ 
            res.push_back(nums);
            return;
        }
        for(int i=left;i> permuteUnique(vector& nums) {
        if(nums.size()==0) return res;
        sort(nums.begin(),nums.end());  // 在进行交换之前,先进行全排列,这样方便过滤掉重复的数字
        help(nums,0);
        return res;
    }
};

D、Top K Frequent Elements(这个题目特别重要)
(返回元素频率排到前K个的元素)
这里的时间复杂度最快的是用到了O(n),因为不管是在unordered_map的统计过程中,还是在拟快排找top k 的过程中,时间复杂度都只是O(n)

class Solution {
public:
    vector topKFrequent(vector& nums, int k) {
        if(k==nums.size()) return nums;
        
         // 算法复杂度小于O(nlogn) 也就意味着不能用排序,但其实排序也没什么用。
        unordered_map record;
        for(int i=0;i> vec;
        for(auto p: record) vec.push_back(make_pair(p.second,p.first));
       
        if(k==vec.size()){
            vector res;
            for(int i=0;i
// 用拟快排的思路去找 top k
        int left=0,right = vec.size()-1;
        while(1){
            int left_ori = left, right_ori = right;
            int mid = left+(right-left)/2;
            swap(vec[mid],vec[left]);
            pair mid_value = vec[left];
            while(left=mid_value.first) right--;
                if(left res;
                for(int i=left;iother){
                left = left_ori;
                right = right-1;
            } 
        }
    }
};

另附1:STL 方法:

class Solution {
public:
    vector topKFrequent(vector& nums, int k) {
        unordered_map freq;
        for (int num : nums) {
            ++freq[num];
        }
        vector> pa;
        for (auto p : freq) {
            pa.push_back(make_pair(-p.second, p.first));
        }
        nth_element(pa.begin(), pa.begin() + k, pa.end());  // 其实在nth_element中用的也是拟快排
        vector res;
        for (int i = 0; i != k; ++i) {
            res.push_back(pa[i].second);
        }
        return res;
    }
};

Tip:STL中的nth_element()方法的使用 通过调用nth_element(start, start+n, end) 方法可以使第n大元素处于第n位置(从0开始,其位置是下标为 n的元素),并且比这个元素小的元素都排在这个元素之前,比这个元素大的元素都排在这个元素之后,但不能保证他们是有序的

另附2:STL 大顶堆的方法:
class Solution {
public:
    vector topKFrequent(vector& nums, int k) 
    {
        vector v;
        unordered_map count_map;
        for(auto n: nums) count_map[n]++;
        priority_queue> maxHeap;
        for(auto& pair: count_map)  maxHeap.emplace(pair.second, pair.first);
        while(k--)
        {
            v.push_back(maxHeap.top().second);
            maxHeap.pop();
        }
        return v;
    }
};

标准做法:

class Solution {
public:
    vector topKFrequent(vector& nums, int k) 
    {
        vector v;
        unordered_map count_map;
        for(auto n: nums) count_map[n]++;
        priority_queue, vector>, greater>> minHeap;
        for(auto& pair: count_map) 
        {
            minHeap.emplace(pair.second, pair.first);
            if(minHeap.size() > k) minHeap.pop();
        }
        while(k--)
        {
            v.push_back(minHeap.top().second);
            minHeap.pop();
        }
        return v;
    }
};

链接:https://discuss.leetcode.com/topic/54560/five-efficient-solutions-in-c-well-explained

E:Missing Number:
(这个题之所以拿出来,是因为需要做原地归位交换)

class Solution {
public:
    int missingNumber(vector& nums) {
        // 这一轮走完,一定能把合适的数放在合适的位置,不合适的数,放在空缺的位置上。
        for(int i=0;i=nums.size()) return i;
        }
        // 如果都没有缺失,那么缺失的就是下一个数。
        return nums.size();
    }
};

F:Maximum Product of Word Lengths
(这里面用到了字符压缩,通过对字符串进行预处理,来判别两个字符串中有没有共同的字母)。方法是按位进行处理):

class Solution {
public:
    int maxProduct(vector& words) {
        int max_length=0;
        vector> vec;
 // 大小是绝对够的,所以用按位或的方式。
        for(int i=0;ilength?max_length:length;
                } 
        }
        return max_length;
    }
};

G:Kth Largest Element in an Array(在一个无序数组中,找到排序情况下第k大的元素)[这个轮子比较重要]

 int findKthLargest(vector& nums, int k) {
        // 如果是第K大,那么肯定是在排好序的第K个下标位置,所以:k = nums.size()-k;
        // ==================================
        // 这个轮子就是如何用拟快排进行排序的,STL方法
        k=nums.size()-k;
        nth_element(nums.begin(),nums.begin()+k,nums.end());
        return nums[k];
        
        // ==================================
        priority_queue 堆排的效率要低很多
        priority_queue p;
        for(int i=0;i=mid_value) right--;
                if(leftk){
                right =right-1;
                left = left_ori;
            }
        }
        return 0;
    }

Tip:
priority_queue 调用 STL里面的 make_heap(), pop_heap(), push_heap() 算法实现,也算是堆的另外一种形式。
优先队列是队列的一种,不过它可以按照自定义的一种方式(数据的优先级)来对队列中的数据进行动态的排序,每次的 push 和 pop 操作,队列都会动态的调整,以达到我们预期的方式来存储。

例如:我们常用的操作就是对数据排序,优先队列默认的是数据大的优先级高,所以我们无论按照什么顺序 push 一堆数,最终在队列里总是 top 出最大的元素。

priority_queue 对于基本类型的使用方法相对简单。他的模板声明带有三个参数: priority_queue
其中 Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。
Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list.
STL 里面默认用的是 vector. 比较方式默认用 operator< , 所以如果你把后面俩个参数缺省的话,
优先队列就是大顶堆,队头元素最大。

用法:示例:将元素5,3,2,4,6依次push到优先队列中,print其输出。

  1. 标准库默认使用元素类型的<操作符来确定它们之间的优先级关系。(大顶堆)
    priority_queue pq;

  2. 数据越小,优先级越高(小顶堆)
    priority_queue, greater >pq;
    http://www.cnblogs.com/flyoung2008/articles/2136485.html

H:Rotate Image(nn;nm)**
[ 给你一个 n*n 的矩阵,将它顺时针旋转90度;]
感觉就是,只有n*n的矩阵,可以在原地进行旋转,但是n*m的矩阵,则必须用另一个矩阵进行置换。

void rotate(vector>& matrix) {
            int num = matrix.size();
            //方法就是先沿着中心线进行对折再沿着反水平线进行对折
            for(int i=0;i

I 蛇形矩阵(考了若干次,还是需要再写一遍),这里有两种蛇形矩阵,一种是正方形,一种是长方形
A):正方形蛇形:

vector> generateMatrix(int n) {
        // 腾讯笔试的时候,是要求按蛇形矩阵的形式进行输出,但是你要输出,也一定要先构建出这样一个对应的矩阵
        vector> vec(n,vector(n));
        // 定义蛇形矩阵的层数
        int level =1;
        // 按圈顺时针对矩阵进行赋值
        int count =1;
        while(level<=n/2){
            // 从level-1 到 n-1-level 水平
            for(int i=level-1;ilevel-1;i--){
                    vec[n-level][i]=count;
                    count++;
            } 
         
            // 从n-level 到 level 逆向竖直
            for(int i=n-level;i>level-1;i--){
                    vec[i][level-1]=count;
                    count++;
            }
            level++;
        }
        
        // 如果是奇数,则填补中间的那个数
        if(n%2==1) vec[level-1][level-1]=count;
        
        return vec;
    }

B):长方形蛇形(长方形受限于边短的那个长度,轮转完了后,需要对 第count 行(如果行数 m 大于列数)的 [count,m-count-1] 或者对第 count 列(如果列数 n 大于行数)的 [count,n-count-1] 进行填充。

J Permutations

(给一个数组,返回它的全排列:方法,深搜+回溯)
class Solution {
public:
    vector> res;
    int size=0;
    void help(vector& nums, vector& vec, int n,int k){
        if(k==nums.size()){
            res.push_back(vec);
            return;
        }
        for(int i=0;i> permute(vector& nums) {
        if(nums.size()==1){
            res.push_back(nums);
            return res;
        }
        // 两种方法吧,第一种是用深搜,第二种是用交换,但是交换和深搜本身差别不大,所以统一用深搜。
        vector tmp;
        size = nums.size();
        for(int i=0;i

K:Remove Duplicates from Sorted Array II
(从排序数组中,移除出现超过3次的数字,并返回符合要求的数组的长度)
不管是出现几次,left 一定得是最直接的判断方式,不能和 left-2 对应的数字相同。

class Solution {
public:
    int removeDuplicates(vector& nums) {
        //总而言之,只要不出现,nums[i]==nums[i-2]的情况就是符合要求的
        int lens = nums.size();
        if(lens<3) return lens;
        int left=2;
        for(int i=2;i

L:3Sum Closest[方法很重要]
(给一个n个整数的数组,在里面找3个数,使得3个数的和最靠近所给的目标值。返回这三个数的和)
// 需要先读懂题,也就是说,这里并没有说,需要连续的三个数
// 三个数的动态变化时,先固定一个,然后另外两个进行左右夹逼

class Solution {
public:
    int threeSumClosest(vector& nums, int target) {
    // 实现排序
    sort(nums.begin(), nums.end()); 
    int res=nums[0]+nums[1]+nums[nums.size()-1]-target; // 这是一个参考值
    // 下面是实现夹逼的方法
    for (unsigned int i=0; i0){  //  当s 大于0 时,得向0靠拢
                if(abs(res)>s) res=s;
                r--;
            } 
            else if (s<0){  //  当s 小于0 时,得向0靠拢
                if(abs(res)>abs(s)) res=s;
                l++;
            } 
            else {//0 和 -1 或者 1 都会到这里来
                if(s==0) return target;
            }
        }
    }
    return target+res;
    }
};

M Combination Sum ii
[给一个候选的数字集合C和一个目标值,找到C中所有的唯一组合,使得组合的数字和为T]
[C中的每一个数字都只能被用一次]
[其实这个题目更难一点,因为不知道有多少种可能]

class Solution {
private:
    vector> res;
public:
    void help(vector& candidates,vector& vec, int target, int left,int sum){
        if(sum==target){
            res.push_back(vec);  
            return;
        } 
        for(int i=left;itarget) return;
            int sign=0;
     // 顺序执行的时候,已经将同样字符的情况考虑到了, 就不需要再提前再考虑了。
            for(int j=left;j> combinationSum2(vector& candidates, int target) {
        if(candidates.size()==0) return res;
        vector vec;
        sort(candidates.begin(),candidates.end());
        help(candidates,vec,target, 0, 0);
        return res;
    }
};

你可能感兴趣的:(leetcode 中等题(部分一))