1. A + B 問題
Note
不使用傳統的“+”運算符,即十進制下的加法,那就從最基礎的二進制加法原理出發。
單獨位相加:0 + 0 = 0, 0 + 1 = 1, 1 + 0 = 0, 1 + 1 = 0。
進位:0 + 0 = 0; 0 + 1 = 0; 1 + 0 = 0; 1 + 1 = 1。
可以發現,單獨位相加符合異或運算的規則,進位符合與運算的規則。加法的結果 = 單獨位 + 進位,因此可以模擬加法運算。由於進位是進到更高位,所以在進位後,要進行一次左移操作,以使進位給到前一位。
class Solution {
public:
/**
* @param a: An integer
* @param b: An integer
* @return: The sum of a and b
*/
int aplusb(int a, int b) {
return (a ^ b) + ((a & b) << 1);
}
};
2. 尾部的零
Note
可以這麼理解:比如,可以知道,任何一個n的階乘,其末尾0的個數取決於因數10的個數。而,由於2出現的次數要遠遠大於5,所以我們只需計算n的階乘的因數中5的個數。這裏便能得出一個結論:
n的階乘的尾部爲0的個數主要取決於其中5的個數。
因爲,所以我們最基礎的想法就是,枚舉1到n,去整除5,能整除計數加1。同時要注意,不是單純地能整除就行,需要對每個枚舉的數都要除5到0爲止。比如25是含兩個5,所以計數是加2。又比如125的因數5個數爲3。
其實更高級一點,我們換個想法。如果我們直接將n除以5,得到的就是n中所有能整除5的數的個數,爲什麼呢?把5理解爲步長就好理解,5,10,15…,是不是呢?將n/5再除以5,得到的就是n中所有能整除25的數的個數;同樣用步長理解,25,50,75…,是不是呢?直到n爲0退出循環。這算法的時間複雜度就降低到了。
class Solution {
public:
/*
* @param n: A long integer
* @return: An integer, denote the number of trailing zeros in n!
*/
long long trailingZeros(long long n) {
long long count = 0;
while (n)
{
n /= 5;
count += n;
}
return count;
}
};
6. 合併排序數組 II
Note
我的想法是將B中的元素逐個的插入A中。當B最後一個元素的值大於A中最後一個元素的值時,剛好可以在 i 到達A.size()前插入完;反之,可在下一個while中將剩餘的B中的元素插入A的末尾。
class Solution {
public:
/**
* @param A: sorted integer array A
* @param B: sorted integer array B
* @return: A new sorted integer array
*/
vector<int> mergeSortedArray(vector<int> &A, vector<int> &B) {
vector<int> C;
int i = 0, j = 0;
while (i < A.size() && j < B.size())
{
if (B[j] < A[i])
A.insert(A.begin() + i, B[j++]);
else
++i;
}
while (j < B.size())
{
A.insert(A.begin() + i, B[j++]);
++i;
}
return A;
}
};
8. 旋轉字符串
Note
題目的意思就是把字符串末尾的offset位字符移到開頭,當然offset要先做處理,大於字符串長度時要減小。我的想法就是將前面的str.length() - offset位字符串給一個新的字符串,然後刪除這一部分,再將新字符串連接到舊字符串末尾。
class Solution {
public:
/**
* @param str: An array of char
* @param offset: An integer
* @return: nothing
*/
void rotateString(string &str, int offset) {
if (str.length() == 0)
return;
string temp;
int end = str.length() - offset % str.length();
temp.insert(temp.begin(), str.begin(), str.begin() + end);
str.erase(str.begin(), str.begin() + end);
str += temp;
}
};
9. Fizz Buzz 問題
Note
兩段代碼都能達到13ms以上,網速夠好的情況下。只有一點要記住,必須先判斷能否同時被3和5整除。
class Solution {
public:
/**
* @param n: An integer
* @return: A list of strings.
*/
vector<string> fizzBuzz(int n) {
vector<string> vecstr;
for (int i = 1; i <= n; ++i)
{
int a = i % 10;
if (i % 3 == 0)
{
if (a == 0 || a == 5)
vecstr.push_back("fizz buzz");
else
vecstr.push_back("fizz");
}
else if (a == 0 || a == 5)
vecstr.push_back("buzz");
else
vecstr.push_back(to_string(i));
}
return vecstr;
}
};
class Solution {
public:
/**
* @param n: An integer
* @return: A list of strings.
*/
vector<string> fizzBuzz(int n) {
vector<string> vecstr;
for (int i = 1; i <= n; ++i)
{
if (i % 3 == 0 && i % 5 == 0)
vecstr.push_back("fizz buzz");
else if (i % 3 == 0)
vecstr.push_back("fizz");
else if (i % 5 == 0)
vecstr.push_back("buzz");
else
vecstr.push_back(to_string(i));
}
return vecstr;
}
};
14. 二分查找
Note
對二分查找進行了一點改進,因爲這是一個存在重複元素的數組,所以一旦發現了同樣的元素,就不再進行查找而是一直往左減小mid直到不再等於target。這點小小的改進竟然使我的耗時成了月榜第一。。。
class Solution {
public:
/**
* @param nums: The integer array.
* @param target: Target to find.
* @return: The first position of target. Position starts from 0.
*/
int binarySearch(vector<int> &nums, int target) {
int mid, left = 0, right = nums.size() - 1;
while (left <= right)
{
mid = (left + right) / 2;
if (nums[mid] == target)
{
while (nums[mid - 1] == target)
--mid;
return mid;
}
else if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
};
–
28. 搜索二維矩陣
Note
兩次二分查找,第一次二分是爲了確定target可能在哪一行,第二次二分是爲了確定target是否存在二維矩陣中。其中要注意,第一次二分後的right,就是它可能存在的行號。其他的,就注意vector<vector> vec的使用方法。vec.size()是一維長度,vec[n].size()是第n維長度。正常表示其中的元素就和普通的二維數組一樣即可,即vec[m][n]。
class Solution {
public:
/**
* @param matrix: matrix, a list of lists of integers
* @param target: An integer
* @return: a boolean, indicate whether matrix contains target
*/
bool searchMatrix(vector<vector<int>> &matrix, int target) {
int len = matrix.size();
if (target > matrix[len - 1][matrix[len - 1].size() - 1] || target < matrix[0][0])
return false;
int mid, left = 0, right = len - 1;
while (left <= right)
{
mid = (left + right) / 2;
if (matrix[mid][0] == target)
return true;
else if (matrix[mid][0] < target)
left = mid + 1;
else
right = mid - 1;
}
int t = right;
left = 0, right = matrix[t].size() - 1;
while (left <= right)
{
mid = (left + right) / 2;
if (matrix[t][mid] == target)
return true;
else if (matrix[t][mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return false;
}
};
35. 翻轉鏈表
Note
正常寫法如第一段代碼所示,用指針p遍歷原鏈表,然後利用頭插法新建一個鏈表並返回頭結點的下一個結點即可。看了討論之後發現了一個更棒的方法,如果我們把鏈表反向指一下,能更快實現鏈表的翻轉。即將 0->1->2->3->4->NULL 變爲 NULL<-0<-1<-2<-3<-4。實現起來很簡單,從頭一個一個往左指(表現爲指向自己的前一個結點)即可,但千萬不要在往左指的過程中丟失了右邊的鏈表。
注意此題沒有頭結點,頭結點是不含任何信息的。
/**
* Definition of singly-linked-list:
*
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/**
* @param head: n
* @return: The new head of reversed linked list.
*/
ListNode * reverse(ListNode * head) {
if (head == NULL)
return head;
ListNode *nhead = new ListNode;
nhead->val = 0;
nhead->next = NULL;
while (head != NULL)
{
ListNode *q = new ListNode;
q->val = head->val;
q->next = nhead->next;
nhead->next = q;
head = head->next;
}
return nhead->next;
}
};
/**
* Definition of singly-linked-list:
*
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/**
* @param head: n
* @return: The new head of reversed linked list.
*/
ListNode * reverse(ListNode * head) {
if (head == NULL)
return head;
ListNode *nhead = NULL;
ListNode *curr = head;
ListNode *p;
while (curr != NULL)
{
p = curr->next;
curr->next = nhead;
nhead = curr;
curr = p;
}
return nhead;
}
};
一定要自己寫一遍哦~~~