程序员进阶之算法练习(八十四)

题目1

题目链接
题目大意:
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。

示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 1e9
-109 <= target <= 1e9

题目解析:
两数之和、三数之和的类似问题,题目要求是要找到所有解。
由于题目对数组元素顺序没有要求,可以先对数组排序,得到从小到大的数组nums;
枚举两个元素a和b,作为四元组的两个元素;剩下两个元素在区间[a, b]中选择。
问题变成了,在区间找到两个数字c和d,并且c+d=target-a-b。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> ret;
        int n = nums.size();
        map<vector<int>, int> h;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 3; j < n; ++j) {
                int m = target - nums[i] - nums[j];
                int left = i + 1, right = j - 1;
                while (left < right) {
                    if (nums[left] + nums[right] == m) {
                        vector<int> tmp = {nums[i], nums[j], nums[left], nums[right]};
                        sort(tmp.begin(), tmp.end());
                        if (!h[tmp]) {
                            ret.push_back(tmp);
                            h[tmp] = 1;
                        }
                        ++left;
                    }
                    else if (nums[left] + nums[right] > m) {
                        --right;
                    }
                    else {
                        ++left;
                    }
                }
            }
        }
        
        return ret;
    }
}ac;

题目2

题目链接
题目大意:
给出一个整数数组nums,数组里每个数字各不相同;
数组原来是以递增的顺序排列如[0,1,2,4,5,6,7],经过变换后,从某个位置开始的元素挪到了数组后半部分,如[4,5,6,7,0,1,2];
现在给出变换后的数组nums和某个数字target,问target在数组中的索引,如果不存在则输出-1;
要求时间复杂度在O(logN)的级别;

Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

题目解析:
在一个有序的数组,查找某个数字的索引,可以通过二分法的方式快速定位;
题目的难点在于数组经过变换后,失去了完整的有序信息;
如果把变换后的数组,用二维坐标系的方式表达,会是是如下的结果:

在两段线段内部,可以使用二分查找。
这里可以用增量查询的方式来定位分段的位置,比如说当前的位置是index;
如果nums[index]<nums[index+1],我们可以认为[index, index+1]是在同一段;
并且如果1满足,则尝试index+1+2,再尝试index+1+2+4,直到不满足之前的条件,则怎么跨段了,此时降级从index+1开始计算;

对于某一段[index, index+k],我们通过上诉的方法判断得到是同一段,接下来在这一段里面查找target;
如果target<num[index] 或者 target > num[index+k],则肯定不在这一段,可以跳过;
如果是其他条件,则同样降级到index+1;

通过这个方法,可以快速定位到target,并且速度非常快!

target < nums[index] 是很重要的剪枝,否则当target在数组中不存在时,很容易降级为O(N)的算法。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        return look(nums, target, 0, 1);
    }
    
    int look(vector<int>& nums, int target, int index, int gap) {
        if (index >= nums.size()) {
            return -1;
        }
        if (nums[index] == target) {
            return index;
        }
        if (index + gap < nums.size() && nums[index + gap] > nums[index] && (nums[index + gap] < target || target < nums[index])) {
            return look(nums, target, index + gap, gap * 2);
        }
        else {
            return look(nums, target, index + 1, 1);
        }
    }
    
}leetcode;

题目3

题目链接
题目大意:
给出一个递增的整数数组nums,求某个整数target在数组的区域,如下:

Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

题目解析:
二分法,给出整数target,可以比较快速找出分别找出左边界,同理找到右边界即可。
时间复杂度:O( log N)

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> ret;
        
        int left = 0, right = (int)nums.size() - 1;
        int index = -1;
        
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                index = mid;
                right = mid - 1;
            }
            else if (nums[mid] < target) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        ret.push_back(index);
        
        if (index == -1) {
            ret.push_back(-1);
            return ret;
        }
        
        
        left = 0, right = (int)nums.size() - 1;
        index = -1;
        
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                index = mid;
                left = mid + 1;
            }
            else if (nums[mid] < target) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        
        ret.push_back(index);
        
        return ret;
    }
}leetcode;

题目4

题目链接
题目大意:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例 1:

输入: [1,3,5,6], 5
输出: 2
示例 2:

输入: [1,3,5,6], 2
输出: 1
示例 3:

输入: [1,3,5,6], 7
输出: 4
示例 4:

输入: [1,3,5,6], 0
输出: 0

题目解析:
经典二分查找,如果数组中有可以直接查找出来。
附加要求,如果数组不存在返回将被插入的位置,这个刚好就是二分的left的位置。

意外发现某个题解
和自己代码一模一样,标点符合和命名都完全一样。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            else if (nums[mid] < target) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        return left;
    }
}lc;

题目5

题目链接
题目大意:
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1 被读作 "one 1" ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2", "one 1" ("一个二" , "一个一") , 即 1211。

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

注意:整数序列中的每一项将表示为一个字符串。

示例 1:

输入: 1
输出: "1"
解释:这是一个基本样例。
示例 2:

输入: 4
输出: "1211"
解释:当 n = 3 时,序列是 "21",其中我们有 "2" 和 "1" 两组,"2" 可以读作 "12",也就是出现频次 = 1 而 值 = 2;类似 "1" 可以读作 "11"。所以答案是 "12" 和 "11" 组合在一起,也就是 "1211"。

题目解析:
按照题目的要求,统计连续的数字数量,然后用sprintf转成字符串,再记录下来。

class Solution {
    vector<string> ans;
public:
    string countAndSay(int n) {
        if (!ans.size()) {
            string s = "1";
            ans.push_back(s);
            for (int i = 1; i < 30; ++i) {
                int cnt = 1;
                string strNew;
                for (int j = 1; j < s.length(); ++j) {
                    if (s[j] == s[j - 1]) {
                        ++cnt;
                    }
                    else {
                        char tmp[11];
                        sprintf(tmp, "%d%c", cnt, s[j - 1]);
                        cnt = 1;
                        strNew.append(tmp, strlen(tmp));
                    }
                }
                char tmp[11];
                sprintf(tmp, "%d%c", cnt, s.back());
                strNew.append(tmp, strlen(tmp));
                s = strNew;
                ans.push_back(s);
            }
        }
        return ans[n-1];
    }
}lt;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容