Leetcode第21题至第30题 思路分析及C++实现

笔者按照目录刷题,对于每一道题,力争使用效率最高(时间复杂度最低)的算法,并全部通过C++代码实现AC。(文中计算的复杂度都是最坏情况复杂度)
因为考虑到大部分读者已经在Leetcode浏览过题目了,所以每道题都按照 解题思路、实现代码 的顺序进行讲解。
(笔者目前已刷 120 题,已更新解法 30 题,最近一段时间会频繁更新)可以点击下方链接,直达gitbook:
https://codernie.gitbooks.io/leetcode-solutions/content/
也欢迎大家关注我的同名微信公众号(大雄的学习人生),有问题可以随时后台回复我,多多探讨。


21. Merge Two Sorted Lists

解题思路

从头至尾一起遍历,每次比较头结点的大小,先添加小的结点,直到某一个链表为空为止,最后再将另一个还不为空的链表添加到末尾即可。

实现代码:

// 21. Merge Two Sorted Lists
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
  ListNode* head = new ListNode(0);
  ListNode* p = head;
  while (l1 != NULL && l2 != NULL) {
    if (l1->val < l2 ->val) {
      p->next = l1;
      l1 = l1->next;
    } else {
      p->next = l2;
      l2 = l2->next;
    }
    p = p->next;
  }
  if (l1 != NULL) {
    p->next = l1;
  } else if (l2 != NULL) {
    p->next = l2;
  }
  return head->next;
}

22. Generate Parentheses

解题思路

回溯法,用一个数组记录【当前字符串,左括号数量,右括号数量】,做 2n 次循环,每次循环遍历当前数组,对左括号不足 n 的添加左括号,右括号不足左括号的添加右括号(确保parentheses是有效的)

实现代码:

// 22. Generate Parentheses O(2^N)
vector<string> generateParenthesis(int n) {
  if (n == 0) return {};
  // nowString, leftCount, rightCount
  vector<pair<string, pair<int, int> > > res = {{"", {0, 0}}};
  vector<pair<string, pair<int, int> > > linshi;
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < res.size(); j++) {
      string oldString = res[j].first;
      if (res[j].second.first < n) {
        linshi.push_back({oldString + '(', {res[j].second.first + 1, res[j].second.second}});
      }
      if (res[j].second.first > res[j].second.second) {
        linshi.push_back({oldString + ')', {res[j].second.first, res[j].second.second + 1}});
      }
    }
    res = linshi;
    linshi = {};
  }
  vector<string> result;
  for (int i = 0; i < res.size(); i++) {
    result.push_back(res[i].first);
  }
  return result;
}

23. Merge k Sorted Lists

解题思路

考虑使用优先队列,这道题相当于21题的加强版,但是如果单纯使用21题的每轮比较法,那么每轮只要要比较 k 次,而我们知道这其中肯定会存在很多重复的比较,所以我们可以使用二叉堆来保存每一轮的比较信息,而在C++中STL已经为我们实现了这种数据结构,那就是优先队列priority_queue,直接调用即可。

实现代码:

// 23. Merge k Sorted Lists
ListNode* mergeKLists(vector<ListNode*>& lists) {
  ListNode *dump = new ListNode(0), *p = dump, *nowPoint;
  // val, node
  priority_queue<pair<int, ListNode*>, vector<pair<int, ListNode*>>, greater<pair<int, ListNode*>> > pQueue;
  for (ListNode* node : lists) {
    if (node != NULL)
      pQueue.push({node->val, node});
  }
  while (!pQueue.empty()) {
    nowPoint = pQueue.top().second;
    p->next = nowPoint;
    p = p->next;
    pQueue.pop();
    if (nowPoint->next != NULL) {
      pQueue.push({nowPoint->next->val, nowPoint->next});
    }
  }
  return dump->next;
}

24. Swap Nodes in Pairs

解题思路

两两交换即可,这里每轮需要变换三个指向,第一个的next要指向第二个的next,第二个的next要指向第一个,前驱的next要指向第二个,每轮过后让 p 指向第一个即可。

实现代码:

// 24. Swap Nodes in Pairs
ListNode* swapPairs(ListNode* head) {
  ListNode *dump = new ListNode(0), *p = dump, *first, *second;
  dump->next = head;
  while (p != NULL && p->next != NULL && p->next->next != NULL) {
    first = p->next;
    second = p->next->next;
    first->next = second->next;
    second->next = first;
    p->next = second;
    p = first;
  }
  return dump->next; 
}

25. Reverse Nodes in k-Group

解题思路

这题相当于24题的加强版,将24题中的2变成了k,换汤不换药,稍微复杂一点,所以建议把每一步的思路理清楚,不然很容易弄错,大的思路是先检查是否还剩下足够数量的结点;然后进行反向操作:反向操作分为三步,中间结点关系反向,头结点指向处理 和 尾结点指向处理;最后让 p 结点后挪即可。

实现代码:

// 25. Reverse Nodes in k-Group
ListNode* reverseKGroup(ListNode* head, int k) {
  if (k == 1) return head;
  vector<ListNode*> nodeList;
  ListNode *dump = new ListNode(0), *p = dump, *former, *latter, *nextLatter, *q;
  dump->next = head;
  while (true) {
    // check count of left nodes
    q = p;
    for (int i = 0; i < k + 1; i++) {
      if (q != NULL) {
        q = q->next;
      } else {
        return dump->next;      
      }
    }
    // deal with medial relations 
    former = p->next;
    latter = former->next;
    for (int i = 0; i < k - 1; i++) {
      // save latter->next as next latter
      nextLatter = latter->next;
      // latter->next = former
      latter->next = former;
      // save latter as next former  
      former = latter;
      latter = nextLatter;
    }
    // deal with head
    q = p->next;
    p->next = former;
    // deal with tail
    q->next = latter;

    p = q;
  }
  return dump->next;      
}

26. Remove Duplicates from Sorted Array

解题思路

按照题意,把数组中不重复出现的数字保存在数组前端即可。

实现代码:

// 26. Remove Duplicates from Sorted Array
int removeDuplicates(vector<int>& nums) {
  if (nums.size() == 0) return 0;
  int j = 1;
  for (int i = 1; i < nums.size(); i++) {
    if (nums[i] != nums[i - 1]) {
      nums[j++] = nums[i];
    }
  } 
  return j;      
}

27. Remove Element

解题思路

这道题和 26 题几乎没有区别,把判断条件稍微改变即可。

实现代码:

// 27. Remove Element
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;
}

28. Implement strStr()

解题思路

字符串匹配算法,选择比较多,最容易的就是暴力匹配,高阶一点可以使用KMP,这里简单起见,采用暴力解法,嘻嘻。

实现代码:

// 28. Implement strStr()
int strStr(string haystack, string needle) {
  if (needle.size() == 0) return 0;
  int hLen = haystack.size(), nLen = needle.size();
  for (int i = 0; i < hLen; i++) {
    if (hLen - i < nLen) {
      return -1;
    } else {
      if (haystack.substr(i, nLen).compare(needle) == 0) {
        return i;
      }
    }
  }
  return -1;
}

29. Devide Two Integers

解题思路

这道题如果用减法去实现会造成时间复杂度过高,从而导致时间溢出,所以这里采用位运算法,从 2 的 31 次方开始除,一直除到 2 的 0 次方,为了防止内存溢出,我们把结果和中间变量保存在 long 类型中的。

实现代码:

// 29. Divide Two Integers
int divide(int dividend, int divisor) {
  if (dividend > INT32_MAX || dividend < INT32_MIN || divisor > INT32_MAX || divisor < INT32_MIN) return INT32_MAX;
  long son = abs((long)divisor), father = abs((long)dividend), res = 0, base = 1, sum = 0;
  for (int i = 31; i >= 0; i--) {
    if (sum + (son << i) <= father) {
      sum += son << i;
      res += base << i;
    }
  }
  if ((dividend >= 0 && divisor > 0) || (dividend < 0 && divisor < 0)) {
    return (res > INT32_MAX) ? INT32_MAX : res;
  } else {
    return (-res < INT32_MIN) ? INT32_MAX : -res;
  }
}

30. Substring with Concatenation of All Words

解题思路

使用哈希表存储 words 数组中各个单词的数量,然后使用暴力匹配即可。

实现代码:

// 30. Substring with Concatenation of All Words
vector<int> findSubstring(string s, vector<string>& words) {
  vector<int> result;
  if (words.size() == 0 || words[0].size() == 0 || s.size() < words.size() * words[0].size()) {
    return result;
  }
  unordered_map<string, int> counts;
  for (string word : words) {
    if (counts.find(word) == counts.end()) {
      counts[word] = 1;
    } else {
      counts[word]++;
    }
  }
  int wordCount = words.size(), wordLength = words[0].size(), strLength = s.size();
  for (int i = 0; i <= strLength - wordLength * wordCount; i++) {
    unordered_map<string, int> innerCounts = counts;
    bool flag = true;
    for (int j = 0; j < wordCount; j++) {
      string nowStr = s.substr(i + j * wordLength, wordLength);
      if (innerCounts.find(nowStr) == innerCounts.end() || innerCounts[nowStr] == 0) {
        flag = false;
        break;
      } else {
        innerCounts[nowStr]--;
      }
    }
    if (flag) {
      result.push_back(i);
    }
  }
  return result;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容