剑指offer学习笔记:6.3 知识迁移能力

面试题38:数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数,例如输出排序数组是{1,2,3,3,3,3,4,5}和数字3,由于3在数组中出现了4次,因此输出为4
leetcode链接 https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/

class Solution {
public:
   int findFirst(vector<int> nums, int target, int begin, int end)
   {
       if (begin > end)
       {
           return -1;
       }
       int mid = begin + (end - begin) / 2;
       if (nums[mid] == target)
       {
           if ((mid == 0) || (nums[mid - 1] !=target))
           {
               return mid;
           }
           else
           {
               return findFirst(nums, target, begin, mid - 1);
           }
       }
       else if (nums[mid] > target)
       {
           return findFirst(nums, target, begin, mid - 1);
       }
       else{
           return findFirst(nums, target, mid + 1, end);
       }
       return -1;
   }
   int findLast(vector<int> nums, int target, int begin, int end)
   {
       if (begin > end)
       {
           return -1;
       }
       int mid = begin + (end - begin) / 2;
       if (nums[mid] == target)
       {
           if ((mid == nums.size() - 1) || (nums[mid + 1] !=target))
           {
               return mid;
           }
           else
           {
               return findLast(nums, target, mid + 1, end);
           }
       }
       else if (nums[mid] > target)
       {
           return findLast(nums, target, begin, mid - 1);
       }
       else{
           return findLast(nums, target, mid + 1, end);
       }
       return -1;
   }
   int search(vector<int>& nums, int target) {
       // 二分法找到target第一次出现的位置和最后一次出现的位置
       int result = 0;
       if (nums.size() == 0)
       {
           return result;
       }
       int last = findLast(nums, target, 0, nums.size() - 1);
       int begin = findFirst(nums, target, 0, nums.size() -1);
       if (last == -1 || begin == -1)
       {
           return 0;
       }
       // cout << "last: " << last << " begin: " << begin << endl;
       result = last - begin + 1;
       return result;
   }
};

解题思路:由于是排序数组,可以用二分法,找到最开始的位置和最后的位置。

面试题39:二叉树深度

输入一棵二叉树的根节点,求该树的深度。
leetcode链接 https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
   int maxDepth(TreeNode* root) {
       int result = 0;
       if (root == NULL)
       {
           return result;
       }
       if (root->left == NULL && root->right == NULL)
       {
           return result + 1;
       }
       if (root->left == NULL)
       {
           return maxDepth(root->right) + 1;
       }
       if (root->right == NULL)
       {
           return maxDepth(root->left) + 1;
       }
       return max(maxDepth(root->left), maxDepth(root->right)) + 1;
   }
};

解题思路:如果一个节点没有左右节点,则其深度为1。若只有右节点,深度为右节点深度+1,若只有左节点,节点深度为左子树+1。若有左右节点,则节点深度为左右子树中较大值+1。

题目2:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的的左子树和右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
   bool isBalanced(TreeNode* root, int& depth)
   {
       if (root == NULL)
       {
            depth = 0;
           return true;  
       }
       int left, right;
       if (isBalanced(root->left, left) && isBalanced(root->right, right))
       {
           if (abs(left - right) <= 1)
           {
               depth = max(left, right) + 1;
               return true;
           }
       }
       return false;
   }
   bool isBalanced(TreeNode* root) {
       if (root == NULL)
       {
           return true;
       }
       if (root->left == NULL && root->right == NULL)
       {
           return true;
       }
       // 通过后序遍历是左右根的特性,一边遍历一边记录,避免重复遍历
       int depth = 0;
       return isBalanced(root, depth);
   }
};

解题思路:
思路1:判断每个节点是不是平衡节点,但是会不停重复遍历很多节点,比较影响性能。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int depth(TreeNode* root)
    {
        if (root == NULL)
        {
            return 0;
        }
        if (root->left == NULL && root->right == NULL)
        {
            return 1;
        }
        if (root->left == NULL)
        {
            return depth(root->right) + 1;
        }
        if (root->right == NULL)
        {
            return depth(root->left) + 1;
        }
        return max(depth(root->left), depth(root->right)) + 1;
    }
    bool isBalanced(TreeNode* root) {
        if (root == NULL)
        {
            return true;
        }
        if (root->left == NULL && root->right == NULL)
        {
            return true;
        }
        int left = depth(root->left);
        int right = depth(root->right);
        // cout << "root: " << root->val << " right: " << right << " left: " << left << endl;
        if (abs(left - right) > 1)
        {
            return false;
        }
        return isBalanced(root->left) && isBalanced(root->right);
    }
};

思路2:采用后序遍历,这样在遍历到根节点的时候,已经遍历过其左右子树,就可以判断当前跟节点是不是平衡节点了。这样就可以一边遍历,一边判断。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isBalanced(TreeNode* root, int& depth)
    {
        if (root == NULL)
        {
             depth = 0;
            return true;  
        }
        int left, right;
        if (isBalanced(root->left, left) && isBalanced(root->right, right))
        {
            if (abs(left - right) <= 1)
            {
                depth = max(left, right) + 1;
                return true;
            }
        }
        return false;
    }
    bool isBalanced(TreeNode* root) {
        if (root == NULL)
        {
            return true;
        }
        if (root->left == NULL && root->right == NULL)
        {
            return true;
        }
        // 通过后序遍历是左右根的特性,一边遍历一边记录,避免重复遍历
        int depth = 0;
        return isBalanced(root, depth);
    }
};

面试题40:数组中只出现一次的数字

一个整型数组内除了两个数字之外,其余数字都只出现了一次,找到这个只出现了一次的数字。
leetcode链接 https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/

class Solution {
public:
   bool checkNIsOne(int n, int index)
   {
       n = n >> index;
       return n & 1;
   }
   int findFirstOne(int n)
   {
       int index = 0;
       while((n & 1) == 0)  // 注意&运算符优先级比==低,要加括号
       {
           n = n >> 1;
           index++;
       }
       return index;
   }
   vector<int> singleNumbers(vector<int>& nums) {
       // 分两步
       // 1.所有数求异或,拿到结果,找到结果二进制表示中第一个为1的位数n
       // 2.按照n位是否为1将数组分为两部分,分别求异或
       vector<int> result;
       if (nums.size() == 0)
       {
           return result;
       }
       int xorVal = 0;
       for(int i = 0; i < nums.size(); i++)
       {
           xorVal  = xorVal  ^ nums[i];
       }
       int index = findFirstOne(xorVal);
       int num1 = 0, num2 = 0;
       for(int i = 0; i < nums.size(); i++)
       {
           if (checkNIsOne(nums[i], index))
           {
               num1 ^= nums[i];
           } else {
               num2 ^= nums[i];
           }
       }
       result.push_back(num2);
       result.push_back(num1);
       return result;
   }
};

解题思路:相同数字异或后为0。先将数组从前往后遍历,求异或值,最终异或结果为两个值出现一次数字的异或结果。以后结果转化为二进制表示,一定有一位是1,找到数字中第一个为1的位,记为n。将数组分为两部分,n位为1的为一个数组,为0的为另一个数组,则两个只出现一次的数组一定分散在两个数组中,且相同数字一定分布在相同数组中。对两个数组分别进行遍历异或,即可得到只出现一次的值。

面试题41:和为s的两个数字vs和为s的连续正数数列

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使其和为s,如果有多对数字和为s,输出任意一对即可
leetcode链接 https://leetcode-cn.com/problems/he-wei-sde-liang-ge-shu-zi-lcof/

class Solution {
public:
   vector<int> twoSum(vector<int>& nums, int target) {
       vector<int> result;
       if (nums.size() == 0)
       {
           return result;
       }
       if (nums.size() == 1)
       {
           if(nums[0] == target)
           {
               return nums;
           }
           else
           {
               return result;
           }
       }
       int begin = 0;
       int end = nums.size() - 1;
       while(begin < end)
       {
           if ((nums[begin] + nums[end]) < target)
           {
               begin++;
           } else if ((nums[begin] + nums[end]) > target)
           {
               end--;
           } else {
               result.push_back(nums[begin]);
               result.push_back(nums[end]);
               break;
           }
       }
       return result;
   }
};

解题思路:双指针。一个指向头,一个指向尾。如果和大于s,尾指针前移。如果和小于s,头指针后移。

题目二:输入一个整数s,打印出所有和为s的连续正整数序列(至少含有两个数)。例如输入15,由于1+2+3+4=4+5+6=7+8=15,因此最终结果打印3个连续序列
leetcode链接 https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/

class Solution {
public:
   vector<vector<int>> findContinuousSequence(int target) {
       vector<vector<int>> result;
       if (target < 1)
       {
           return result;
       }
       if (target == 3)
       {
           vector<int> tmp;
           tmp.push_back(1);
           tmp.push_back(2);
           result.push_back(tmp);
           return result;
       }
       int small = 1, big = 2;
       int sum = 3;
       while((big-small) >= 1 || big == 2)
       {
           // cout << big << " " << small << " sum: " << sum << endl;
           if (sum < target)
           {
               big++;
               sum = sum + big;
           }
           else if (sum == target)
           {
               vector<int> tmp;
               for(int i = small; i <= big; i++)
               {
                   tmp.push_back(i);
               }
               result.push_back(tmp);
               big++;
               sum = sum + big;
           } else {
               sum = sum - small;
               small++;
           }
       }
       return result;
   }
};

解题思路:两个指针,small指针初始化为1,bigger指针初始化为2。求small-big之间序列的和,如果和小于s,增大bigger,如果和大于s,增加small。直到bigger-smaller=1,停止。过程表示为

步骤 small big 序列 序列和 与s相比 下一步操作
1 1 2 1,2 3 小于 增大big
2 1 3 1,2,3 6 小于 增大big
3 1 4 1,2,3,4 10 大于 增大small
4 2 4 2,3,4 9 等于 打印序列,增大big
5 2 5 2,3,4,5 14 大于 增大small
6 3 5 3,4,5 12 大于 增大small
7 4 5 4,5 9 等于 打印序列,停止

面试题42:翻转单词vs左旋字符串

输入一个英文句子,翻转句子中单词的顺序,但是单词中的字符顺序不变。为简单起见,标点符号的处理和普通字母相同。例如输入为“I am a student." 输出为”student. a am I“。
leetcode链接 https://leetcode-cn.com/problems/reverse-words-in-a-string/

class Solution(object):
   def reverse(self, s):
       tmp = ""
       for i in range(1, len(s) + 1):
           tmp += s[len(s) - i]
       return tmp
   def reverseWords(self, s):
       """
       :type s: str
       :rtype: str
       """
       r = self.reverse(s)    # 整个句子进行翻转
       result = ""
       items = r.split(" ")
       for item in items:
           if item == '':
               continue
           result += self.reverse(item)   # 每个单词进行翻转,翻转回去
           result += ' '
       result = result.strip(' ')
       return result

解题思路:先翻转整个句子。再按照空格分隔,翻转每个单词。最后就是只有单词顺序发生了翻转。递归实现。涉及字符串分割问题,写python了。主要是看此路,不然就直接

class Solution(object):
    def reverseWords(self, s):
        """
        :type s: str
        :rtype: str
        """
        result = ""
        items = s.split(" ")
        for i in range(1, len(items)):
            if items[len(items) - i] == '':
                continue
            result += items[len(items) - i]
            result += " "
        result += items[0]
        result = result.strip(" ")
        return result

题目二:字符串的左旋操作就是把字符串前面的若干字符转移到字符串的尾部。请定义一个函数,实现左旋操作。比如字符串是"abcdefg",返回该函数左旋两位得到的结果"cdefgab"。
leetcode链接 https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/

class Solution {
public:
   string reverse(string s, int begin, int end)
   {
       string result = "";
       if (begin > end)
       {
           return "";
       }
       for(int i = end; i >= begin; i--)
       {
           result += s[i];
       }
       return result;
   }
   string reverseLeftWords(string s, int n) {
       string result;
       if (s.length() == 0)
       {
           return result;
       }
       string head = reverse(s, 0, (n-1) % s.length());
       string tail = reverse(s, (n-1) % s.length() + 1, s.length() - 1);
       result = reverse(head + tail, 0, s.length() - 1);
       return result;
   }
};

解题思路:将字符串分为两部分。n%len长度和剩余长度,分别进行旋转。譬如例子中n=2,则旋转后结果bagfedc,再将字符串整体进行翻转,得到cdefgab,就是最终的结果。这个方法思路和代码都很简洁,但是效率低。其实可以确定旋转点,将旋转点前面的子串移到后面去,就可以了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342