LeetCode个人刷题笔记EZ篇

1.Easy
map中,map[一个不存在的key]返回整型Int0。程序容易出错在如果第一格是正确答案也是0.
因此可以用map.find(key) == map.end()来做。

3.Easy
整数最大范围:Integer.Max_VALUE

27.Easy
用迭代器来删除erase数组中的某些元素:注意删除的那一次循环不要再it++了。因为在进行单个元素删除后,传入的迭代器指向不变,仍然指向被删除元素的位置,而被删除元素之后的所有元素都向前移动一位,也就是该迭代器实际上是指向了原来被删除元素的下一个元素。

for(auto it = vec.begin();it != vec.end();)
{
	if(*it == value)
		vec.erase(it);
	else
		it++;
}

136.只出现一次的数字 (异或)
方法一:先排序,然后每次跳两格遍历。如果当前元素不等于后面一个元素,直接返回。如果全等于,就会遍历到最后一个元素,这时最后一个元素一定是落单的,返回最后一个元素。 并且,做一个return -1的异常捕获。
方法二:把所有数字做异或运算,最后结果就是落单的。
异或 :相同结果为0,不同结果为1
a 异或0 = a;
a异或a = 0;
异或符号 : a ^ b

137.环形链表 (快慢指针,哈希表)
利用快慢指针:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == NULL || head->next == NULL)
            return false;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(slow != fast)
        {   
            if(fast == NULL || fast->next == NULL)
                return false;
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

哈希法:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        map<ListNode* , int> m;
        while(head)
        {
            m[head]++;
            if(m[head] > 1) return true;//重复出现立刻跳出
            head = head->next;
        }
        return false;
    }
};

141.环形链表 双指针跳转
因为俩链表不同长度,所以用alen + blen = blen + alen的特性解决问题。在it1 = NULL时,另it1 = headB。当第二次又走到末尾的时候,it1和it2同时为NULL。
两个NULL是相等的,所以自动跳出了循环。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* it1 = headA;
        ListNode* it2 = headB;
        if(it1 == NULL || it2 == NULL) return NULL;
        while(it1 != it2)
        {
            if(it1 == NULL)
                it1 = headB; 
            else  
                it1 = it1->next;     
            if(it2 == NULL)
                it2 = headA;
            else 
                it2 = it2->next;
        }
        return it1;
    }
};

167.两数之和[2] 哈希表,双指针
除了用1中的哈希表方法,还可以用头尾双指针来做。
因为序列已经是有序的了,所以将两数和sum和target判断,如果相等则推入result数组,如果大于则尾指针前移,如果小于则头指针后移。

class Solution {
public:
	vector<int> twoSum(vector<int>& numbers, int target) {
		vector<int> res;
		int i = 0, j = numbers.size() -1;
		while (i <= j)
		{	
			int sum = numbers[i] + numbers[j];
			if (sum == target)
			{
				res.push_back(i + 1);
				res.push_back(j + 1);
				return res;
			}
			else if (sum < target)
				i++;
			else if (sum > target)
				j--;
		}
		return res;
	}
};

169.多数元素 哈希表,分而治之(暂时不会)

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        map<int,int> m;
        int n = nums.size();
        for(int i = 0;i < n;i++)
        {
            if(m.find(nums[i])==m.end() )
                m[nums[i]] = 1;
            else
                m[nums[i]]++;
        }
        for(auto it = m.begin();it != m.end();it++)
        {
            if(it->second > n/2)
                return it->first;
        }
         return -1;
    }
};

202.快乐数 投机取巧(设置循环N次跳出),快慢双指针(标准解法):快指针多调用一次getnext()即多跑了一格

class Solution {
public:
    bool isHappy(int n) {
        int tmp = n;
        int cnt = 0;
        while(tmp != 1)
        {   
            int sum = 0;
            while(tmp != 0)
            {   
                sum += (tmp % 10)*(tmp % 10);
                tmp = tmp / 10;
            }
            tmp = sum;
            cnt++;
            if(cnt > 1000) return false;
        }
        return true;
    }
};

注意:多调用一次getnext()等于多跑了一格,调用的对象也要注意,快慢指针分别有不同的速度。

class Solution {
public:
    int getNext(int n)
    {
        int sum = 0;
        while(n != 0)
        {   
            sum += (n % 10)*(n % 10);
            n = n / 10;
        }
        return sum;
    }

    bool isHappy(int n) {
        int slowR = n;
        int fastR = getNext(n);
        while(fastR != 1 && slowR != fastR)
        {
            slowR = getNext(slowR); //慢指针速度针对慢指针对象
            fastR = getNext( getNext(fastR));
        }
        return fastR == 1;//fast跳出时,若不是1则铁定有循环了
    }
};

189.旋转数组 环状替代,reverse的妙用(更方便)
环状替代基本思路想出来了,但是没法自己处理n是k的整数倍的问题。题解用了一个外层循环的start指针替代,内层循环回到一次原点则跳出到外层,外层指针加一。

public class Solution {
    public:
     void rotate(int[] nums, int k) {
        k = k % nums.size();
        int count = 0;
        for (int start = 0; count < nums.length; start++) {
            int current = start;
            int tmp1 = nums[start];
            do {
                int next = (current + k) % nums.length;
                int tmp2 = nums[next];//把目标位置的值保存
                nums[next] = tmp1;//把该放在这个位置的值(来自于上个位置的)放好
                tmp1 = tmp2;//把要放置的值更新
                current = next;//指针移到下个正确的位置
                count++;
            } while (start != current);//回一次原点,则原点+1,保证可以遍历所有数组。如果不是n不是k的整数倍,则在内层循环中就搞完了
        }
    }
}

用reverse做(推荐):
先整体颠倒,然后颠倒前K个,再颠倒剩下的部分,就是结果了。
注意reverse里面的是指针范围是左闭右开的。左边的截取到你写的那个指针,右边的截取到你写的结尾指针的前一个。(就比如说标准的vector的末尾end()指针是指向结尾的那个空指针,闭区间的取值规则正好可以取到最后一个元素)

class Solution {
public:
	void rotate(vector<int>& nums, int k) {
        k = k% nums.size();
		reverse(nums.begin(), nums.end());
		reverse(nums.begin(), nums.begin() + k);
		reverse(nums.begin() + k , nums.end());
	}
};

53.最大自序和 贪心算法
PTA中出现过类似的。遍历一遍的同时算sum,如果sum小于0则直接抛弃,把sum归零。否则一直加下去,用result保存每个出现过的sum里面最大的值。
注意:INT_MIN才是最小的整数,-2147483648;INTMAX_MIN不知道是啥玩意,调试中显示为0;

class Solution {
public:
	int maxSubArray(vector<int>& nums) {
		int len = nums.size();
		int res = INT_MIN;
		int sum = 0;
		if (nums.size() == 1) return nums[0];
		for (int i = 0; i<len; i++)
		{
			sum += nums[i];
			res = max(res, sum);
			if (sum < 0)
				sum = 0;
		}
		return res;
	}
};

66.加一 insert的用法
vector.insert(加在这个位置的前面,你要加的元素)
或者
void insert(目标位置,你要加的片段的开头,你要加的片段的结尾);

class Solution {
public:
	vector<int> plusOne(vector<int>& digits) {
		int len = digits.size();
		int jin = (digits[len - 1] + 1) < 10 ? 0 : 1;
		digits[len - 1] = ( (digits[len - 1] + 1)% 10);
		for (int i = len - 2; i >= 0; i--)
		{
			int tmp = digits[i]  + jin;
			jin = tmp / 10;
			digits[i] = tmp % 10;
		}
		if (jin == 1)
			digits.insert(digits.begin(), 1);
		return digits;

	}
};

67.二进制求和 str.append(你要补几个,你要补的元素)
PTA中出现过类似题,用颠倒后补全的方法做较优(空间超过100%,时间超过80%)
用到append函数来补0。

class Solution {
public:
	string addBinary(string a, string b) {
		int lenA = a.length(), lenB = b.length();
		reverse(a.begin(), a.end());
		reverse(b.begin(), b.end());
		if (lenA > lenB)
			b.append(lenA - lenB, '0');
		else
			a.append(lenB - lenA, '0');
		int jin = 0;
		for (int i = 0; i < a.length(); i++)
		{	
			int sum = (a[i] - '0') + (b[i] - '0') + jin;
			a[i] = (sum % 2 + '0');
			jin = sum / 2;
		}
		if (jin == 1)
			a.insert(a.end(), '1');
		reverse(a.begin(), a.end());

		return a;
	}
};

58.最后一个单词的长度 字符串处理
从后往前找,从第一个不是空格的字符开始计数,再遇到空格就返回。

class Solution {
public:
	int lengthOfLastWord(string s) {
		int sum = 0, len = s.length();
		int flag = 0;//0为还没开始一个单词,1为开始了
		for (int i = len - 1 ; i >= 0; i--)
		{
			if (!isalpha(s[i]) && flag == 0)
				continue;
			else if (isalpha(s[i]))
			{
				flag = 1;
				sum++;
			}
			else if (!isalpha(s[i]) && flag == 1)
				return sum;
		}
		return sum;
	}
};

121.买卖股票最佳时机 贪心法,类似前面的最大子列和这题

class Solution {
public:
	int maxProfit(vector<int>& prices) {
		int result = INT_MIN;
		int sum = 0;
		for (int i = 1; i < prices.size(); i++)
		{
			int tmp = prices[i] - prices[i - 1];//单日利润
			sum += tmp;//从买入日算起的总和利润
			if (sum > 0)
				result = max(result, sum);//记录总利润最大值
			else
				sum = 0;//如果总利润为负了,抛弃之前的买入日,从下一个日子重新开始计算
		}
		if (result != INT_MIN)
			return result;
		else
			return 0;

	}
};

203.移除链表元素 哨兵,指针结点删除与释放。
引入哨兵可以规避空指针的情况特殊情况,头哨兵是一种通用的方法。
删除指针的时候是,如果不删除,才令it = it->next这样移动,不然的话删除的那一次循环是不进行移动的,因为如果移动删除同时进行,删除掉的结点后面的那个结点相当于没做判断了。

class Solution {
public:
	ListNode* removeElements(ListNode* head, int val) {
		ListNode* dummy = new ListNode(0);//头哨兵
		dummy->next = head;

		ListNode* it = dummy;
		while ( it ->next != NULL)
		{
			if (it->next->val == val)
			{
				ListNode* tmp = it->next;
				it->next = it->next->next;
				delete tmp;
			}
			else
				it = it->next;
		}
		return dummy->next;
	}
};

204.计数质数 IsPrime,厄拉多塞筛法
IsPrime效率低下,用厄拉多塞筛选,由小到大遍历数组。每次排除一个数字的倍数,并改变标记,剩下的数就全是素数了。
注意:vector的初始化方法–vec(你要初始化多少个格子,你要舒适化的数值)

class Solution {
public:
	int countPrimes(int n) {
		int count = 0;
		vector<int> sign(n,0);
		for (int i = 2; i < n; i++)
		{
			if (sign[i] == 0)
			{
				count++;
				for (int j = i + i; j < n; j = j+i)
					sign[j] = 1; //排除掉这个数的所有倍数,并改标记
			}
		}
		return count;
	}
};

205.同构字符串 hashmap,str.find()
妙用str.find()返回某个字符在str中第一次出现的位置。也就是说如果不同构,必然那轮循环返回的位置不一样。

//方法一:双哈希表做法  时间15%,内存100%
class Solution {
public:
	bool isIsomorphic(string s, string t) {
		if (s.length() != t.length()) return false;
		map<char, char> sign1;
		map<char, char> sign2;
		for (int i = 0; i < s.length(); i++)
		{
			if (sign1.find(t[i]) == sign1.end() && sign2.find(s[i]) == sign2.end())//如果没找到
			{
				sign1[t[i]] = s[i];
				sign2[s[i]] = t[i];
			}
			else
			{	
				if (s[i] != sign1[t[i]] || t[i] != sign2[s[i]])
					return false;
			}
		}
		return true;
	}
};
//方法二:用str.find()返回特性--返回第一次出现这个字符的位置  时间89%,空间100%
class Solution {
public:
	bool isIsomorphic(string s, string t) {
		if (s.length() != t.length()) return false;
		for (int i = 0; i < s.length(); i++)
		{
			if (s.find(s[i]) != t.find(t[i]))
				return false;
		}
		return true;
	}
};

206.翻转链表 迭代用双指针,递归需要多看多理解记住。
迭代(更好理解且常用):
一前一后俩指针,由于next要断裂,所以需要预先储存好next的指针值。然后再一次挪动俩指针即可。

class Solution {
public:
	ListNode* reverseList(ListNode* head) {
		ListNode* pre = head;
		ListNode* cur = NULL;
		while ( pre != NULL)
		{
			ListNode* tmp = pre->next;
			pre->next = cur;
			cur = pre;
			pre = tmp;
		}

		return cur;
	}
};

递归:
最先直接到12345的第5层(因为5的next就是空了),返回4的层数后,这里面的P就是5这个节点本身。随后用5->next->next也就是5指向4,4指向空(这么做是为了1最后指向空)。然后4由返回3的层数。以此类推知道本身为空结束。

 class Solution {
 public:
	 ListNode* reverseList(ListNode* head) {
		 if (head == NULL || head->next == NULL) {
			 return head;
		 }
		 ListNode* p = reverseList(head->next);
		 head->next->next = head;
		 head->next = NULL;
		 return p;
	 }
 };

你可能感兴趣的:(LeetCode)