一.位运算总结规律
1.与运算
1)判断num的奇偶:
num & 1 = 1 (num奇数)
num & 1 = 0 (num偶数)
2)(num-1)& num
将num的最后一位1消去变成0(可用于统计num有多少个1)
2.异或运算
1)偶数个num相异或,结果为0;
奇数个num相异或,结果为num。
用公式代替:
num ^ num = 0;
num ^ b ^ num = b;
2)交换两数(不使用中间变量)
a = a^b;
b = a^b;
a = a^b;
另外,还可以用加减法/乘除法(容易出现溢出问题)
a = a+b;
b = a-b;
a = a-b;
3.取反运算
1)一个数与其相反的数相加,结果为-1
a + ~a = -1
T1:二进制中1的个数
题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
概念:
正数的补码 = 原码
负数的补码 = 原码按位取反(除最高位)+ 1
正数右移,最高位补0;负数右移,最高位补1;
思路:
方法1:将这个数“按位与”1,再将1左移。之所以不将该数右移,是因为要考虑到负数,负数右移,高位补1,改变了1的数量。
方法2:假设某数为n,那么n&(n-1)得到的效果就是将该数的低位中的1变0。如此循环,n中有多少个1,就可以变换多少次
方法2比方法1省时,方法1必须移动32次,而方法2只要没有1了就停止了(改变了原数n)。
方法3:(python)用split统计字符串中的某个字符个数
print(len(str.split(‘x’))-1)
方法1:
int NumberOf1(int n)
{
int num = 1;
int count = 0;
while(num != 0)
{
if((num & n) != 0)
count++;
num <<= 1;
}
return count;
}
方法2:
int NumberOf1(int n)
{
int count = 0;
while(n)
{
n &= n-1;
count++;
}
return count;
}
T2:数值的整数次方
题目:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0
寻常的for/while/递归做法,时间复杂度O(n),循环n次,乘n次。当n极大时,很明显上面的方法就很慢了,还可能超时。所以采用其它的算法。
方法:二分幂。时间复杂度为O(logn)。为了求2^ 8,可以先求2^ 4;为了求2^ 4,可以先求2^2,如此循环直至n=1。
小技巧:
1)判断奇偶。num % 2 == 1可以用位运算代替。num & 1 == 1 -> 奇数;num & 1 == 0->偶数。
2)减半。power=power/2 可以用位运算代替。
power >>= 1;
float pow(int a, int n)//二分幂,定义成float类型
{
if (n == 0)
return 1;
if (n == 1)
return a;
int mut = pow(a, n / 2);
mut *= mut;
if (n % 2 == 1)
mut *= a;
return mut;
}
double Power(double base, int exponent)
{
if (exponent >= 0) //幂为正数
return pow(base, exponent);
return 1 / pow(base, -exponent); //幂为负数,为了避免结果为0,上面的函数定义成float
}
T3:只出现一次的数字
题目:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
思路:奇数个相同的数进行异或,结果为本身,偶数个进行异或结果为0.
0 ^ num = num;
num ^ num = 0;
int singleNumber(vector<int>& nums)
{
int res = 0;
int length = nums.size();
for(int i = 0; i < length; i++)
{
res ^= nums[i];
}
return res;
}
T4:不用加减乘除做加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
两个数异或:相当于每一位相加,而不考虑进位;
两个数相与,并左移一位:相当于求得进位;
int Add(int num1, int num2)
{
while (num1)
{
int a = (num1 & num2) << 1;
int b = num1 ^num2;
num1 = a;
num2 = b;
}
return num2;
}
二. 利用指针移动(链表思想)
将一串有规律的数字当成链表,用指针pre+next来控制移动。
将链表当作一个窗口,将双指针当作一把尺子,从前往后移动。
T1. 和为S的连续正数序列
题目描述:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列。
方法1:
思路:既然是连续的正数序列,那么将这一串数字想象成链表,并用两个指针pre,next线性移动(也就是剑指offer里大神说的“双指针+窗口”)。
vector<vector<int> > FindContinuousSequence2(int sum)
{
vector<vector<int> > A;
int pre = 1, next = 2; //pre和next从1和2开始移动
while (pre < next)
{
int curSum = (pre + next)*(next - pre + 1) / 2;
if (curSum < sum) next++; //小于
else if (curSum == sum) //等于
{
vector<int>a;
for (int i = pre; i <= next; i++)
{
a.push_back(i);
}
pre++;
A.push_back(a);
}
else if (curSum > sum) pre++; //大于
}
return A;
}
方法2:
思路:我的方法,找了规律,比较牵强。从组成个数i考虑,i为奇数/偶数,i为奇数的话,中间值mid=sum/2,那么左右两边的数也好确定;i为偶数,情况多一些。
等差数列的求和公式,充分不必要条件(first+last)* i / 2 = sum,可以得出i的范围,减少时间复杂度
i是整数个数,若存在连续i个整数相加为sum,必定满足2*sum%i==0,不满足就不用考虑这个i了。
i之所以从3开始,不从2开始,是因为sum=奇数时,需要考虑i=2的这种情况,而sum=偶数时,不存在两个连续正数相加为sum,不必讨论,所以将i=2单独放到最后讨论。
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > A;
for (int i = sqrt(2 * sum); i >= 3; i--) //充分不必要,i有范围
{
if (2 * sum % i == 0)
{
int mid = sum / i;
vector<int> a;
int j;
for (j = (i-1) / 2; j >= 1; j--) //传入mid左边的数,注意顺序
{
a.push_back(mid - j);
}
a.push_back(mid); //传入mid
for (j = 1; j <= (i-1) / 2; j++) //传入mid右边的数,
{
a.push_back(mid + j);
}
if (sum % i != 0) // 此时i为偶数个,比奇数个多添加一个元素。
{
a.push_back(mid + j);
}
if (a.size() != i) continue; //用于排除“sum%i ==0时,可能存在,也可能不存在”中的不存在的情况 例如i=4,sum=100,求出来的连续整数24,25,26,但不符合实际情况,i和a.size不等
A.push_back(a);
}
}
if (sum / 2 >=1 && sum % 2 == 1) //讨论i=2的这种情况,i=2时,有且只有奇数sum存在这种情况,偶数sum中不存在两个连续整数相加的这种组合。并且排除sum=1
{
int mid = sum / 2;
vector<int> a;
a.push_back(mid);
a.push_back(mid + 1);
A.push_back(a);
}
return A;
}
T2. 和为S的两个数字
题目描述:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路:同T1的思路,双指针+夹逼准则
题目要求输出两个数乘积最小的,由于数组递增,pre是从最小的数开始遍历,一旦找到sum=pre+next,即可退出,不用继续查找所有满足“和等于S”的pre和cur。
从下图可知,当sum=0,找出了三对,第一对-16是三队中结果最小的,不管是用正负数结合还是纯正数/纯负数,外层的积都要小于内层的积,所以只要找到第一对就可以退出,不用继续找其它满足条件的对数
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
int len = array.size();
int pre = 0, next = len-1; //注意此处,next从尾开始,pre从头
vector<int> a;
while (pre < next)
{
int curSum = array[pre] + array[next];
if (curSum < sum) pre++;
if (curSum == sum)
{
a.push_back(array[pre]);
a.push_back(array[next]);
return a;
}
if (curSum > sum)
{
next--;
}
}
return a;
}
我的方法,与夹逼方法不同的是,我的方法中的pre和next是从头开始,不是从两边夹击。
vector<int> FindNumbersWithSum(vector<int> array, int sum) {
int len = array.size();
int pre = 0, next = 1; //不同处1
vector<int> a;
while (pre < next && next <= len)
{
int curSum = array[pre] + array[next];
if (curSum < sum) next++; //不同处2
if (curSum == sum)
{
a.push_back(array[pre]);
a.push_back(array[next]);
return a;
}
if (curSum > sum) //不同处3
{
pre++;
next = pre + 1;
if ((sum - array[pre]) < array[next]) break;
}
}
return a;
}
T3. 链表中倒数第k个节点
题目描述:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
思路: 如果用传统的链表的做法也可以,用双指针做法会更有效。
- 方法1(传统思想):先从头遍历得出链表长度length,再从头开始遍历,直到第length-k个节点;或者将链表节点push到stack,再从stack依次弹出第k个节点(若用python,push完后,采用切片,直接return stack[-k]即可,不用依次弹出)。
- 方法2:递归。递归到head=NULL,再开始计数,一直到count=k,就可以将当前的node存入临时变量中。
- 方法3:双指针(推荐)。将这个链表看作是一个长方形窗口,将双指针看作是一把长度为k的尺子,尺子从头开始往后移动,当尺子的前端滑到窗口的低端,那么尺子的后端所指向的位置就是k所在的位置。看图如下:
方法1:代码略。
方法2:递归
class Solution {
public:
ListNode* temp; //临时变量,最终的返回值
int countKth(ListNode* head, int k)
{
if (head == NULL) return 0;
int count = countKth(head->next, k) + 1;
if (count == k)
temp = head;
return count;
}
ListNode* getKthFromEnd2(ListNode* head, int k) {
if (k < 1) return NULL;
countKth(head, k); //不需要接收返回值
return temp;
}
};
方法3:双指针+窗口
ListNode* getKthFromEnd3(ListNode* head, int k) {
if (k<1) return NULL;
ListNode* pre = head;
ListNode* cur = head; //此时还未形成一把尺子,因为起点一样,尺子长度为0
int copy = k;
while (copy > 1)
{
cur = cur->next;
copy--;
} //形成一把尺子,长度为k
while (cur->next) //形成尺子之后,开始移动尺子,直到这把尺子前端移动到窗口最右端,停止。
{
cur = cur->next;
pre = pre->next;
}
return pre;
}
T4. 链表相交
题目描述:输入两个链表,找到它们的第一个公共交点。
示例1:
注意:判断两个链表是否相交,是基于结点的引用,而不是基于结点的值。也就是说,如果一个链表的第K个结点与另一个链表的第J个结点是同一个结点(引用相同),那么它们相交。注意上面的第二张图,公共交点是8,不是1,因为两个链表中的结点1的地址不同,虽然从表面上看,val相同,链接的下一个结点也是8。所以在代码中,判断条件是 if(curA == curB),而不是if(curA->val == curB->val).
思路:方法1:见下图
方法2:双指针。
将两个链表头尾串联,a,b分别表示链表A和链表B不相交的结点长度,n表示它们相交部分的结点长度,相交的部分用蓝色阴影表示。在图中,假设B长,其实双指针移动与它们的长度并没有什么关系)
代码:
ListNode *getIntersectionNode2(ListNode *headA, ListNode *headB)
{
ListNode* curA = headA;
ListNode* curB = headB;
while (curA != curB)
{
curA = curA == NULL ? headB : curA->next;
curB = curB == NULL ? headA : curB->next;
}
return curA;
}
T5. 链表的中间结点
题目描述:给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
示例1 :输入:[1,2,3,4,5]
输出 :返回结点 3
示例2 :输入:[1,2,3,4,5,6]
输出 :返回结点 4 (由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。)
思路:双指针,慢指针走1步,快指针走两步,快指针走到底后,慢指针恰好到链表中间。
代码:
ListNode* middleNode(ListNode* head) {
ListNode* pre = head;
ListNode* cur = head;
while (cur && cur->next)
{
pre = pre->next;
cur = cur->next->next;
}
return pre;
}
T6. 回文链表
题目描述:编写一个函数,检查输入的链表是否是回文的。
示例 1:
输入:1->2
输出: false
示例 2:
输入:1->2->2->1
输出: true
思路:其实就是判断该链表是否对称(以中间结点),差不多这个意思吧。至于长度为偶数和长度为奇数是否有影响,其实不影响的。例如1->2->3->2->1,将pre定位到3后,反转3->2->1成1->2->3,此时比较前半部分以head开头的1->2和后半部分以pre开头的1->2->3,while(head && pre),自然是不会比较到3的。当长度为偶数时,更容易比较,不做讨论。
方法1:反转链表的后半部分,然后将链表的前半部分和后半部分一一进行对比是否val相等。用到的知识点基本都是其它链表算法题的综合,知识点1:用双指针定位到链表的中间结点;知识点2:反转链表(我用的是反转链表的第二种方法,详见 "目录四. 链表” )
代码:
bool isPalindrome(ListNode* head) {
if (head == NULL) return true; //空链表为回文链表
//定位到中间结点
ListNode* cur = head;
ListNode* pre = head;
while (cur && cur->next)
{
pre = pre->next; //走一步
cur = cur->next->next; //走两步
}
//反转中间结点的后面部分
cur = pre;
ListNode* next = pre->next;
while (next)
{
cur->next = next->next;
next->next = pre;
pre = next;
next = cur->next;
}
//开始对比以head开头的前半部分后以pre开头的后半部分
while (head && pre)
{
if (head->val == pre->val)
{
head = head->next;
pre = pre->next;
}
else
return false;
}
return true;
}
方法2:引入堆栈,简单。将所有的值存入栈中,由于stack本身存在先入后出的规律,所以存入stack顶端的结点是链表中最后面的结点,从stack顶端开始至下,与链表从前往后一一比较。
代码:
bool isPalindrome2(ListNode* head)
{
stack<int>stack;
ListNode* cur = head;
while (cur)
{
stack.push(cur->val);
cur = cur->next;
}
while (!stack.empty())
{
int temp = stack.top();
if (head->val == temp)
{
stack.pop();
head = head->next;
}
else
return false;
}
return true;
}
三. 字符串
T1. 字符流中第一个不重复的字符
题目描述:
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
思路:利用哈希表将字符本身作为下标索引,不用数字‘0,1,2…’ 做下标索引。
class Solution
{
public:
//Insert one char from stringstream
void Insert(char ch)
{
str += ch; //字符串拼接
hash[ch]++; //以字符本身为索引
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce()
{
for(int i=0;i<str.size();i++)
{
if(hash[str[i]] == 1)
return str[i]; //此时返回的一定是只出现一次的字符
}
return '#';
}
private:
string str;
char hash[256] = {0};
};
四. 链表
T1. 反转链表
题目描述:反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
方法1:三个指针,改变每个节点的指向,图示如下:
代码:
ListNode* reverseList(ListNode* head) {
ListNode* pre = NULL;
ListNode* cur = head;
ListNode* next = head;
while (cur)
{
next = next->next; //步骤1
cur->next = pre; //2
pre = cur; //3
cur = next; //4
}
return pre;
}
方法2:原本链表顺序为:1->2->3->4,现在将2挪到1之前,变成2->1->3->4,该步骤图示如下,然后再修改,将3挪到2之前,变成3->2->1->4,再修改,变成4->3->2->1。
代码:
ListNode* reverseList2(ListNode* head) {
if(head == NULL) return head;
ListNode* cur = head;
ListNode* next = head->next; //当head为NULL,如果没有上面的if判断语句,执行到这步会报错,因为head->next不存在。
ListNode* pre = head;
while (next)
{
cur->next = next->next; //步骤1
next->next = pre; //2
pre = next; //3
next = cur->next; //4
}
return pre;
}
T2. 删除链表中重复的结点
分三类题型:
题型一:删除有序链表中重复的结点,重复结点只保留一个
题目描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 :输入: 1->1->2
输出: 1->2
代码:
ListNode* removeDuplicateNodes(ListNode* head) {
if (head == NULL || head->next == NULL) return head;
ListNode* cur = head;
while (cur->next)
{
if (cur->val == cur->next->val)
{
cur->next = cur->next->next;
}
else
{
cur = cur->next;
}
}
return head;
}
题型二:删除有序链表中重复的结点,重复结点一个不留
题目描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
示例:1->2->3->3->4->4->5
输出: 1->2->5
思路:重新生成头节点,是为了避免出现1->1->2->3这种第一个结点就出现重复,需要删除的情况,如果删除了,那么return
pHead时就会出现丢失的情况。代码图示如下:
代码:
ListNode* deleteDuplication(ListNode* pHead)
{
if (pHead == NULL || pHead->next == NULL) return pHead;
ListNode* newHead = new ListNode(0); //重新生成一个头节点,val为0
newHead->next = pHead;
ListNode* pre = newHead;
ListNode* cur = newHead->next;
while (cur)
{
if (cur->next && cur->val == cur->next->val)
{
while (cur->next && cur->val == cur->next->val)
{
cur = cur->next; // 定位重复的结点中的最后一个
}
pre->next = cur->next;
cur = cur->next;
}
else
{
pre = pre->next;
cur = cur->next;
}
}
return newHead->next;
}
题型三:删除未排序链表中的重复结点,重复出现的结点只保留一个
题目描述:
移除未排序链表中的重复节点。保留最开始出现的节点。
示例::1-> 2-> 3-> 3-> 2-> 1
输出: 1->2->3
提示:链表长度在[0, 20000]范围内。链表元素在[0, 20000]范围内。
思路:
方法1:没有根据提示采用的方法,不具有特殊性,双层for循环的思路,代码中用两个while替代双for,时间复杂度为O(n^2),费时。
方法2:采用缓冲区,创建一个长度为20000的hash,具有特殊性。用hash的下标标记每一个结点的val出现的次数,对于重复出现的结点,跳过,链表的next不再链接这个结点。时间复杂度为O(n).。hash简单使用如图所示:
代码:方法1:
ListNode* removeDuplicateNodes2(ListNode* head) {
ListNode* pre = head;
ListNode* cur = head;
while (pre)
{
cur = pre;
while (cur->next)
{
if (pre->val == cur->next->val)
cur->next = cur->next->next;
else
cur = cur->next;
}
pre = pre->next;
}
return head;
}
方法2:
ListNode* removeDuplicateNodes3(ListNode* head)
{
if (head == NULL) return head;
int hash[20001] = { 0 }; //因为链表长度在[0, 20000]范围内
ListNode* cur = head;
hash[cur->val] = 1;
while (cur->next)
{
if (hash[cur->next->val] == 0)
{
hash[cur->next->val]++;
cur = cur->next;
}
else
cur->next = cur->next->next;
}
return head;
}
T3. 删除链表指定结点
题目描述:给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
示例 :4->5->1->9, val = 5
输出:4->1->9
说明:
题目保证链表中节点的值互不相同
若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
注意:如果链表为 4->5->1->9, val = 4,第一个结点就是要删除的,不能直接删除,需要构造一个头节点newHead,
newHead的next 指向原链表的 head,见代码。由于链表结点的val互不相等,那么只要满足pre->next->val == val的条件,就可以return链表**
代码:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* newHead = new ListNode(0); //构造头节点只是为了避免第一个结点就是要删除的结点
newHead->next = head;
ListNode* pre = newHead;
while (pre->next)
{
if (pre->next->val == val)
{
pre->next = pre->next->next;
return newHead->next;
}
else
pre = pre->next;
}
return NULL;
}
T4. 删除链表元素
题目描述:删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
思路:T4与T3类似,也是构造表头,以防第一个结点就是要删除的结点。利用双指针/单指针均可,使用单指针的话,可以参考T3。
ListNode* removeElements(ListNode* head, int val) {
ListNode* newhead = new ListNode(0);
newhead->next = head;
ListNode* pre = newhead;
while (head)
{
if (pre->next->val == val)
{
pre->next = head->next;
}
else
{
pre = head;
}
head = head->next;
}
return newhead->next;
}
T5. 合并有序链表
题目描述:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路:三指针,cur1和cur2边移动边比较,这两个指针仅仅做大小上的比较,各自在各自的链表上从前往后移动,谁小,谁就往自己的那个链表尾端移动,大的那个先不动。而pre负责指向,串联起两个链表,在两个链表间来回移动。
ListNode* mergeTwoLists1(ListNode* l1, ListNode* l2) {
if (l1 == NULL && l2 == NULL) return NULL;
if (l1 == NULL && l2) return l2;
if (l1 && l2 == NULL) return l1;
ListNode* head = new ListNode(0);
ListNode* cur1 = l1;
ListNode* cur2 = l2;
ListNode* pre = head;
if (cur1->val <= cur2->val)
head->next = cur1;
else
head->next = cur2;
while (cur1 && cur2)
{
cur1->val <= cur2->val ? (pre->next = cur1, cur1 = cur1->next) : (pre->next = cur2,cur2 = cur2->next);
pre = pre->next;
}
if (cur1)
pre->next = cur1;
if (cur2)
pre->next = cur2;
return head->next;
}
五. 数组
T1. 旋转数组的最小数字
题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路:
- 方法1:python中,可以直接min(array) 。我觉得还是避免这种做法,因为主要还是考思维,这么做就没什么意义。
- 方法2:先排序,再取第一个。C++: sort(array.begin(),array.end());
python: sorted(array) 或者 array.sort()- 方法3:二分法,从中间开始比较,略
- 方法4:双指针。类似于快速排序的第一轮,慢指针从头开始,快指针从尾开始,向中间靠拢,总会找到最小的那个。代码比二分法的简洁,好理解(我还是比较喜欢这个方法,因为二刷这题的时候,是一瞬间蹦出来的想法,以前第一次做这题,还是比较死脑筋的那种做法)。
int minNumberInRotateArray(vector<int> rotateArray) {
int len = rotateArray.size();
int low = 0;
int high = len - 1;
while(low < high)
{
while(low < high && rotateArray[low] >= rotateArray[high])
low++;
while(low < high && rotateArray[high] > rotateArray[low])
high--;
}
return rotateArray[low];
}
其它
T1. 数据流中的中位数
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路:先排序,再分奇偶讨论。其中排序使用的是sort直接排序,也可以边push边排序。
边push边排序的思路是:先push一个num到队列中,比较list[i]和list[i-1],若不满足大小顺序,两数交换,然后将i–,再次比较list[i]和list[i-1],直到到达list中的第一个元素,然后再push一个num到队列,再次进行从后往前的比较。
注意点:
- 根据奇偶性来判断输出一个值还是两个值和的平均,可以用一句代码代替(可以避免if else的奇偶判断): return (list[(size - 1) >> 1] + list[size >> 1]) / 2.0;
- 如果要输出浮点型,需要/2.0,而不是/2。
- 如果要输出浮点型,可以将vector < int > list 修改成vector< float >list;
方法一:使用sort
class Solution {
vector<int> list;
public:
void Insert(int num)
{
list.push_back(num); //直接无顺序push
}
double GetMedian()
{
sort(list.begin(), list.end());
int mid_index = list.size() / 2;
if (list.size() & 1 == 1) // 奇数
{
return list[mid_index];
}
return (list[mid_index] + list[mid_index-1]) / 2.0; //之所以是2.0而不是2,是因为需要返回double类型
}
};
方法二:边push边排序
class Solution2 {
vector<int> list;
int size;
public:
void Insert(int num)
{
list.push_back(num);
size = list.size();
for (int i = size - 1; i > 0; i--)
{
if (list[i] < list[i - 1])
{ //实现两数交换
list[i] = list[i] ^ list[i - 1];
list[i - 1] = list[i] ^ list[i - 1];
list[i] = list[i] ^ list[i - 1];
}
}
}
double GetMedian()
{
//return (list[(size - 1) >> 1] + list[size >> 1]) / 2.0;
return (size & 1 == 1) ? list[size / 2] : (list[size / 2] + list[size / 2 - 1]) / 2.0;
}
};