Tips:++i 的执行速度比i++快,与之对应的另一个单元运算符也是如此
题目
链表部分
简单难度
1. 链表求和
描述:
你有两个用链表代表的整数,其中每个节点包含一个数字。数字存储按照在原来整数中相反的顺序,使得第一个数字位于链表的开头。写出一个函数将两个整数相加,用链表形式返回和。
样例:
输入: 7->1->6->null, 5->9->2->null
输出: 2->1->9->null
样例解释: 617 + 295 = 912, 912 转换成链表: 2->1->9->null
方法:
第一步:新建一个链表,使用尾插法建表。
第二步:将两个链表对应位置依次相加,并用一个变量来保存相加结果同时利用这个变量来判断是否有进位。假设该变量为a,a%10即为对应位置的数值,a/10即可得到是否有进位。
第三步:把数插进链表中
第四步:判断两个提供数据的链表是否为空以及是否有进位,只要有一个链表不为空或者有进位则继续进行上述操作。
演示:
243+564=807
2 4 3
5 6 4
进行逐位相加
7 10 7
这时候第二位有进位(根据常识,某一位上的数只能是0-9)
进行%10操作,得到第二位的数值并存入新链表中,再进行/10操作,得到进位
这样进行下一轮计算的时候,a就不是0了,而是1。这样就可以达到将最后一位修正的目的。
最终结果
7 0 8
AC代码:
/**
* 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 l1: the first list
* @param l2: the second list
* @return: the sum list of l1 and l2
*/
ListNode *head = new ListNode;
ListNode * addLists(ListNode * l1, ListNode * l2) {
ListNode *head = new ListNode(0);//建立头结点
ListNode *p = head;//头指针
int carry = 0;//保存加法结果,并作为判断是否有进位的依据
while(true) {
if(l1!=nullptr){//只要l1不为空就进行
carry+=l1->val;
l1=l1->next;
}
if(l2!=nullptr) {//只要l2不为空就进行
carry+=l2-> val;
l2=l2->next;
}
p->val=carry%10;//将相加后得到的数放入新建的链表中
carry/=10;//获得仅为信息
if(l1!=nullptr||l2!=nullptr||carry!=0) {//只要两链表有一个不为空或者有进位继续进行循环
p=(p->next=new ListNode(0));//分配新的节点
} else//l1,l2全为空并且没有进位循环结束
break;
}
return head;//一定要返回,否则OJ得到的结果永远为NULL或提示你segement fault
}
};
2. 翻转链表
描述:
翻转一个链表
样例:
输入: 1->2->3->null
输出: 3->2->1->null
方法:
第一步:新建一个链表结点(声明为nullptr)用来存储从原链表上摘下来的结点(倒过来的尾插法)。
第二步:进行摘结点的操作:
① 在循环体中创建一个临时结点,用于存储当前结点的下一个结点避免结点丢失。
② 让当前结点(也就是头指针指向的结点)的后继结点指向第一步新建的结点。
③ 让新建的链表的表指针指向新插入的结点
④ 把原链表的头指针还回去,让它指向临时结点指向的下一个节点。
第三步:返回新生成的链表
AC代码:
/**
* 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) {
ListNode *pre = nullptr;//用于存储摘下来的结点
while(head!=nullptr){
ListNode *temp = head->next;//用于存储头结点的下一个结点,防止丢失
head->next = pre;//让头结点的后继指向新建的结点
pre=head;//让新建链表的指针指向新插入结点
head=temp;//归还头指针
}
return pre;
}
};
3. 删除排序链表中的重复元素
描述:
给定一个排序链表,删除所有重复的元素每个元素只留下一个。
样例:
样例 1:
输入: null
输出: null
样例 2:
输入: 1->1->2->null
输出: 1->2->null
样例 3:
输入: 1->1->2->3->3->null
输出: 1->2->3->null
方法:
只需要将当前结点的值与下一结点的值进行比较,如果相同,则将下一结点跳过,否则将指针移动到下一节点(注意开头判空)
注意循环条件一定是p->next不为空,因为要与下一结点进行比较,如果下一结点为空,则会提示segement fault。而且,最后一定要返回头结点,否则永远为NULL
AC代码:
/**
* 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: head is the head of the linked list
* @return: head of linked list
*/
ListNode * deleteDuplicates(ListNode * head) {
if(head==nullptr) {
return nullptr;
}
ListNode *p = head;//新建一指针用于循环,保持头指针不动
while(p->next!=nullptr){
if(p->val == p->next->val) {//进行比较,值相同跳过,不相同指向下一结点
p->next = p->next->next;
}else{
p=p->next;
}
}
return head;
}
};
4. 链表化数组
描述:
将一个数组变成链表
样例:
例1:
输入: [1,2,3,4],
输出: 1->2->3->4->null.
例2:
输入: [1,2],
输出: 1->2->null.
方法:
把数串起来就行。但要注意判空,以及这个是一个不带头结点的单链表。
AC代码:
/**
* Definition of ListNode
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/*
* @param nums: an integer array
* @return: the first node of linked list
*/
ListNode * toLinkedList(vector<int> &nums) {
if(nums.size() == 0)
return nullptr;
ListNode *head = new ListNode(nums[0]);
head->next = nullptr;
ListNode *p = head;
for (int i = 1; i < nums.size(); ++i) {
p->next = new ListNode(nums[i]);
p=p->next;
}
return head;
}
};
5. 无序链表的重复项删除
描述:
设计一种方法,从无序链表中删除重复项。
样例:
样例 1:
输入:1->2->1->3->3->5->6->3->null
输出:1->2->3->5->6->null
样例 2:
输入:2->2->2->2->2->null
输出:2->null
方法:
因为水平有限,只能想到一种最笨的方法。采用三指针,双重循环方案。
一开始先判空:判断第一个结点是否为空,第一个结点有没有后继结点。因为如果没有后继,那么将无法进行比较。
① 一个position指针指向当前需要被比较的结点,一个q指针指向position指针的下一个结点,一个p指针指向q的前驱结点。
② 当两相邻结点的值相等的时候,让q的前驱结点p的后继指向q后继结点,即p->next = q->next。同时注意释放此时q指针指向的结点。然后让q向后挪动一个位置。
③ 当两相邻接点不等的时候,将q向后挪动一个位置,同时让p指向q刚刚指向的结点。因为p始终指向q的前驱结点,
④ 比较完成后,让position后移一个位置。
最后,一定要返回头指针,不然OJ获得的结果永远为空
注意:
p和q一定要放在第一重循环中更新。不然会出现结点跳跃等诡异到让人难以理解(与预期完全不符)的结果
AC代码:
/**
* 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: The first node of linked list.
* @return: Head node.
*/
static ListNode *removeDuplicates(ListNode *head) {
if (head == nullptr || head->next == nullptr)//判空,注意判空条件
return head;//注意返回head,而不是无脑返回nullptr(可能导致错误)
ListNode *position = head;//用于记录当前被比较的结点
while (position!= nullptr) {
ListNode *p=position;//指示q的前驱结点
ListNode* q=position->next;//指向用于比较的结点
while (q!= nullptr) {
if(position->val == q->val) {//如果两结点的值相等
ListNode *temp = q;//定义一个临时结点存储当前p指向的结点
p->next = q->next;//把现在p指向的结点跳过
delete temp;//释放重复结点所占用的空间
} else{
p = q;//因为两结点的值不相同,所以将q向后挪动一位,并同步将p向后挪动一位
}
q = p->next;//让q指向p现在的后继结点,也就是下一个需要比较的结点
}
position = position->next;//将position指针后移一位
}
return head;
}
};
6. 在排序链表中插入一个节点
题目详情
描述:
在链表中插入一个节点。
样例:
样例 1:
输入:head = 1->4->6->8->null, val = 5
输出:1->4->5->6->8->null
样例 2:
输入:head = 1->null, val = 2
输出:1->2->null
方法:
将要插入的值与链表中的值一次比较,找到合适的位置,插入。
① 判空。对原本为空的链表进行处理,即:新建一个结点用来存储要插入的值,然后让这个结点指向原本的头结点(也就是传入为nullptr的那个),这样新结点的后继就变成了nullptr。此时返回新结点即可得到结果。
② 考虑边界情况。插入的值小于等于所有链表中的值。那么直接进行一个判断,然后把新结点插入到最前边(与①的操作相同),然后返回新结点。
③ 考虑正常情况,要插入的值在链表的中间位置进行插入。先判断是否还有后继结点,因为如果要插入的值大于当前结点的值要向后迭代,如果后继结点为空那么继续进行迭代则会引发内存错误。然后判断当前值是否大于后继结点的值。因为边界情况(也就是直接插入第一个结点的情况)已经在②中解决了那么,所以第一个结点无需再次比较,比较它的后继结点即可。
AC代码:
/**
* 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: The head of linked list.
* @param val: An integer.
* @return: The head of new linked list.
*/
ListNode * insertNode(ListNode * head, int val) {
ListNode *q = new ListNode(val);
q->next = nullptr;
if(head == nullptr) {
return q;
}
ListNode *p = head;
ListNode *m = nullptr;
if (p->val>= val) {
q->next = head;
return q;
}
while(p ->next != nullptr && val > p->next->val) {
p=p->next;
}
q->next = p->next;
p->next =q;
return head;
}
};
7. 删除链表中的元素
描述:
删除链表中等于给定值 val 的所有节点。
样例
样例 1:
输入:head = 1->2->3->3->4->5->3->null, val = 3
输出:1->2->4->5->null
样例 2:
输入:head = 1->1->null, val = 1
输出:null
方法:
注意这个题是不带头结点的单链表,为了方便操作我们需要把它扩充为带头结点的单链表,这样可以有效解决第一个结点就相等的情况带来的麻烦。.
因为原题给的是用类定义的结点,所以声明方法如下:
ListNode listHead;
listHead.next = head;
head = &listHead;
剩下的就很简单了,只需要依次比较找出相同结点并将其从链表中去掉即可。
AC代码:
/**
* 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 a ListNode
* @param val an integer
* @return a ListNode
*/
ListNode *removeElements(ListNode *head, int val) {
ListNode dummy;
dummy.next = head;
head = &dummy;
while (head->next != nullptr) {
if (head->next->val == val) {
head->next = head->next->next;
} else {
head = head->next;
}
}
return dummy.next;//在类中定义的时候next为指针型变量,此时next指向头结点,所以返回next;
}
};
8. 合并两个排序链表
描述:
将两个排序链表合并为一个新的排序链表。
样例
样例 1:
输入: list1 = null, list2 = 0->3->3->null
输出: 0->3->3->null
样例2:
输入: list1 = 1->3->8->11->15->null, list2 = 2->null
输出: 1->2->3->8->11->15->null
方法:
创建一个新的带头结点的单链表用于存放结点,然后进行题目中给出的两链表的值比较。
如果l1<l2的值,那么把l1的当前结点插入新的单链表中,否则把l2的当前节点插入新的单链表中。
AC代码:
/**
* 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 l1: ListNode l1 is the head of the linked list
* @param l2: ListNode l2 is the head of the linked list
* @return: ListNode head of linked list
*/
ListNode * mergeTwoLists(ListNode * l1, ListNode * l2) {
ListNode *merge = new ListNode(0);//定义新单链表的头结点
ListNode *temp = merge;//定义一个指向头结点的头指针
while(l1!=nullptr&&l2!=nullptr) {
if(l1->val<l2->val) {
temp->next = l1;//l1<l2把l1插入
l1=l1->next;//向后迭代
}else {
temp->next = l2;//l1>l2把l2插入
l2=l2->next;//向后迭代
}
temp = temp->next;//移动到新插入的节点上
}
if(l1==nullptr) {//l1为空,把l2的剩余部分全部插入进去
temp->next = l2;
}else{//l2为空,把l1的剩余部分全部插入进去
temp->next = l1;
}
return merge->next;//返回头结点的后继结点
}
};
9. 相反的顺序存储
描述:
给出一个链表,并将链表的值以倒序存储到数组中。
注意:
- 不能改变原始链表的结构
- ListNode 有两个成员变量:ListNode.val 和 ListNode.next
样例
样例1
输入: 1 -> 2 -> 3 -> null
输出: [3,2,1]
样例2
输入: 4 -> 2 -> 1 -> null
输出: [1,2,4]
方法:
利用一个数组即可实现。先循环遍历列表,确定链表长度。然后建立一个与之大小相等的数组用于存放结点的数值。得到数组后,再把数组赋值给vector即可得到结果。
AC代码:
/**
* 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: the given linked list
* @return: the array that store the values in reverse order
*/
vector<int> reverseStore(ListNode * head) {
if(head == nullptr) {//判空,并对空情况进行处理
vector<int> a;
return a;
}
ListNode *p = head;//下面的一小段进行遍历,确定链表长度
int num = 0;
while(p!=nullptr) {
++num;
p=p->next;
}
int s[num];//确定链表长度后,建立与之大小相等的数组,再把链表中的数值逆序存入
p = head;
for(int i = num - 1; i >= 0; --i) {
s[i] = p->val;
p=p->next;
}
vector<int> m;//把得到的数组赋值给vector
for (int k = 0; k < num; ++k) {
m.push_back(s[k]);
}
return m;//返回vector即可得到结果
}
};
10. 删除链表中倒数第n个节点
描述:
给定一个链表,删除链表中倒数第n个节点,返回链表的头节点。
注意:
- 链表中的节点个数大于等于n
样例:
样例 1:
输出: list = 1->2->3->4->5->null, n = 2
输出: 1->2->3->5->null
样例 2:
输入: list = 5->4->3->2->1->null, n = 2
输出: 5->4->3->1->null
方法
给原来单链表添加一个头结点,然后遍历单链表得到单链表长度L。然后从头遍历L - n - 1个长度,去掉这一结点的后继结点即可达成目的。
原因:假设有5个结点,要删除倒数第二个结点,则向后挪动两次指针即可找到要被删除的结点的前驱结点。
AC代码:
/**
* 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: The first node of linked list.
* @param n: An integer
* @return: The head of linked list.
*/
ListNode * removeNthFromEnd(ListNode * head, int n) {
if(head == nullptr){
return head;
}
ListNode *q = new ListNode(0);
q->next = head;
head = q;
int counts = 0;
while(q!=nullptr) {
q=q->next;
++counts;
}
q = head;
for(int i = 0; i < counts - n - 1; i++) {
q = q->next;
}
q->next = q->next->next;
return head->next;
}
};
11. 两两交换链表中的节点
描述:
给一个链表,两两交换其中的节点,然后返回交换后的链表。
注意:
- 你的算法只能使用常数的额外空间,并且不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
样例:
样例 1:
输入:1->2->3->4->null
输出:2->1->4->3->null
样例 2:
输入:5->null
输出:5->null
方法:
给原先的链表添加一个头结点,然后进行常规的链表交换操作就可达到目的。但要注意交换时的逻辑,不要写错。
AC代码:
/**
* 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: a ListNode
* @return: a ListNode
*/
ListNode * swapPairs(ListNode * head) {
if(head==nullptr || head->next == nullptr) {//判空
return head;
}
ListNode *phead = new ListNode(0);//添加头结点
phead->next = head;
ListNode *p = phead;//添加头指针
while(p->next != nullptr && p->next->next != nullptr) {
ListNode *temp = p->next->next;//把靠后的结点摘下来
p->next->next = temp->next;//让靠后结点的后继结点不至于丢失
temp->next = p->next;//常规交换
p->next = temp;
p=p->next->next;
}
return phead->next;
}
};
12. 链表的中点
描述:
找链表的中点。
样例:
样例 1:
输入: 1->2->3
输出: 2
样例解释: 返回中间节点的值
样例 2:
输入: 1->2
输出: 1
样例解释: 如果长度是偶数,则返回中间偏左的节点的值。
方法:
使用快慢指针的方法来寻找终点。
可以确保时间复杂度是 O(n/2)
快指针每次两步,慢指针每次一步。
快指针到尾的时候,慢指针就是中点。
AC代码:
/**
* 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: the head of linked list.
* @return: a middle node of the linked list
*/
ListNode * middleNode(ListNode * head) {
if(head == NULL)
return NULL;
ListNode*p=head;
int num = 0;
while(p!=NULL) {
p=p->next;
num++;
}
num = (num +1)/2;
while(head!=NULL) {
if(num == 1) {
return head;
}
--num;
head=head->next;
}
}
};
13. 链表倒数第n个节点
题目详情
描述:
找到单链表倒数第n个节点,保证链表中节点的最少数量为n。
样例:
样例 1:
输入: list = 3->2->1->5->null, n = 2
输出: 1
样例 2:
输入: list = 1->2->3->null, n = 3
输出: 1
方法:
先遍历链表得到表长L,然后从头开始,往后移动L-n个位置。再用一个新的链表结点保存这一个节点的值,并返回新建的结点。
AC代码:
/**
* Definition of ListNode
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/*
* @param head: The first node of linked list.
* @param n: An integer
* @return: Nth to last node of a singly linked list.
*/
ListNode * nthToLast(ListNode * head, int n) {
if(head == nullptr) {
return head;
}
ListNode *p = head;
int counts = 0;
while(p!=nullptr) {
++counts;
p=p->next;
}
p=head;
for(int i =0; i < counts - n; i++) {
p=p->next;
}
ListNode *s = new ListNode(p->val);
return s;
}
};
14. 两数相乘
题目详情
描述:
给出两个链表形式表示的数字,写一个函数得到这两个链表相乘乘积。
样例:
样例 1:
输入:9->4->6->null,8->4->null
输出:79464
解释:946*84=79464
样例 2:
输入:3->2->1->null,1->2->null
输出:3852
解释:321*12=3852
方法:
依次遍历两个链表。在遍历的时候用一个变量a来保存这链表表示的数值。每次循环时,a*=10,a+=val。这样就完成了数值还原。
AC代码:
/**
* 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 l1: the first list
* @param l2: the second list
* @return: the product list of l1 and l2
*/
long long multiplyLists(ListNode * l1, ListNode * l2) {
if(l1 == nullptr || l2 == nullptr) {
return 0;
}
ListNode *pointerA = l1;
long numberA = 0;
while(pointerA != nullptr) {
numberA *= 10;
numberA += pointerA->val;
pointerA = pointerA->next;
}
ListNode *pointerB = l2;
long numberB = 0;
while(pointerB !=nullptr) {
numberB *= 10;
numberB += pointerB->val;
pointerB = pointerB->next;
}
return numberA * numberB;
}
};
15. 链表的中间结点
描述:
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
注意:
- 给定的结点数值在1-100之间
样例:
样例 1:
输入:1->2->3->4->5->null
输出:3->4->5->null
样例 2:
输入:1->2->3->4->5->6->null
输出:4->5->6->null
方法:
使用快慢指针
AC代码:
/**
* 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: the head node
* @return: the middle node
*/
ListNode * middleNode(ListNode * head) {
if(head == nullptr) {
return head;
}
ListNode *fast = head;
ListNode *low = head;
while(fast!=nullptr && fast->next !=nullptr) {
fast = fast->next->next;
low = low->next;
}
return low;
}
};
16. 链表的反向带权和
题目详情
描述:
给定一个链表,求出这个链表的带权和。链表结点的权值指的是该结点到链表尾节点的结点数。
样例:
样例1
输入: 3 -> 2 -> 5 -> 1
输出: 29
解释:
(3 * 4 + 2 * 3 + 5 * 2 + 1) = 29
样例2
输入: 1 -> 2 -> 3 -> 4
输出: 20
解释:
(1 * 4 + 2 * 3 + 3 * 2 + 4) = 20
方法:
先遍历一次链表得到链表长度,然后把链表对应位置的值与权值相乘即可得到结果。
AC代码:
/**
* 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: the given linked list
* @return: the weighted sum in reverse order
*/
int weightedSumReverse(ListNode * head) {
if(head == nullptr)
return 0;
ListNode *p = head;
int counts = 0;
while(p!=nullptr) {
counts++;
p=p->next;
}
p=head;
int num = 0;
while(p!=nullptr) {
num += p->val * counts;
--counts;
p=p->next;
}
return num;
}
};
17. 在O(1)时间复杂度删除链表节点
题目详情
描述:
给定一个单链表中的一个等待被删除的节点(非表头或表尾)。请在在 O(1) 时间复杂度删除该链表节点。
样例:
样例 1:
输入:
1->2->3->4->null
3
输出:
1->2->4->null
样例 2:
输入:
1->3->5->null
3
输出:
1->5->null
方法:
传统意义上,删除当前节点是从头结点head遍历,匹配到目标节点后删除,但是时间复杂度是O(n)。如果是删除下一个节点,时间复杂度则是O(1),因此考虑将下一个节点立面的data数据拷贝到当前节点(当前节点是带删除节点,其中数据可以覆盖),然后删除下一个节点。
AC代码:
/**
* Definition of ListNode
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/*
* @param node: the node in the list should be deleted
* @return: nothing
*/
void deleteNode(ListNode * node) {
if(node==NULL) {
return;
}
ListNode *p = node->next;
node->val = p->val;
node->next = p->next;
delete p;
}
};
18. 队列维护
题目详情
描述:
实现一个队列的操作
- enqueue(item).将新元素放入队列中。
- dequeue(). 将第一个元素移出队列,返回它。
样例:
例1:
输入:
enqueue(1)
enqueue(2)
enqueue(3)
dequeue() // return 1
enqueue(4)
dequeue() // return 2
例2:
输入:
enqueue(10)
dequeue()// return 10
方法:
一个简单的入队出队操作。需要注意,本题没有给出结点类的定义,需要自己补充。注意结点类的实现方式,不同于结构体。而且在队列操作类中也没有给出队头队尾指针,都需要自己定义。first = last = nullptr。
判断队列是否为空可以使用 !first 。原因:nullptr与NULL在本质上都表示0,取非就是1,所以可以作为判断条件。
AC代码:
class Node {
public:
int val;
Node *next;
Node(int val_) {
this->val = val_;
this->next = nullptr;
}
};
class MyQueue {
public:
/*
* @param item: An integer
* @return: nothing
*/
Node *first, *last;
MyQueue() {
first = last = nullptr;
}
void enqueue(int item) {
if (!first) {
last = new Node(item);
first = last;
} else{
last->next = new Node(item);
last = last->next;
}
}
/*
* @return: An integer
*/
int dequeue() {
int m = first->val;
auto k = first;
first = first->next;
delete k;
return m;
}
};
19. 链表划分
描述:
给定一个单链表和数值x,划分链表使得所有小于x的节点排在大于等于x的节点之前。
你应该保留两部分内链表节点原有的相对顺序。
样例:
样例 1:
输入: list = null, x = 0
输出: null
样例解释: 空链表本身满足要求
样例 2:
输入: list = 1->4->3->2->5->2->null, x = 3
输出: 1->2->2->4->3->5->null
样例解释: 要保持原有的相对顺序。
方法:
采用双指针left和right。小于给定值的放到left指针后,大于等于的放到right指针后。
AC代码:
/**
* 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: The first node of linked list
* @param x: An integer
* @return: A ListNode
*/
ListNode *partition(ListNode *head, int x) {
ListNode *leftDummy = new ListNode(0);
ListNode *rightDummy = new ListNode(0);
ListNode *leftTail = leftDummy;
ListNode *rightTail = rightDummy;
while (head != NULL) {
if (head->val < x) {
leftTail->next = head;
leftTail = head;
} else {
rightTail->next = head;
rightTail = head;
}
head = head->next;
}
leftTail->next = rightDummy->next;
rightTail->next = NULL;
return leftDummy->next;
}
};
数组部分
简单难度
1.合并排序数组 II
题目详情
描述:
合并两个有序升序的整数数组A和B变成一个新的数组。新数组也要有序。
样例:
样例 1:
输入: A=[1], B=[1]
输出:[1,1]
样例解释: 返回合并后的数组。
样例 2:
输入: A=[1,2,3,4], B=[2,4,5,6]
输出: [1,2,2,3,4,4,5,6]
样例解释: 返回合并后的数组。
方法:
使用两个变量分别作为A,B数组的下标变量,然后逐个比较大小。小的那个放入新的数组。
AC代码:
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;
int j=0;
while(i < A.size() && j < B.size()) {
if(A[i] > B[j]) {
C.push_back(B[j]);
j++;
}else if(B[j] > A[i]){
C.push_back(A[i]);
i++;
}else{
C.push_back(A[i]);
C.push_back(B[j]);
i++;
j++;
}
}
while(i<A.size()) {
C.push_back(A[i]);
++i;
}
while(j < B.size()) {
C.push_back(B[j]);
++j;
}
return C;
}
};
2.数组划分
描述:
给出一个整数数组 nums 和一个整数 k。划分数组(即移动数组 nums 中的元素),使得:
所有小于k的元素移到左边,所有大于等于k的元素移到右边
返回数组划分的位置,即数组中第一个位置 i,满足 nums[i] 大于等于 k。
注意:你应该真正的划分数组 nums,而不仅仅只是计算比 k 小的整数,如果数组 nums 中的所有元素都比 k 小,则返回 nums.length。
样例:
例1:
输入:
[],9
输出:
0
例2:
输入:
[3,2,2,1],2
输出: 1
解释:
真实的数组为[1,2,2,3].所以返回 1
方法:
使用双指针。指针i指向数组开头,指针j指向数组结尾。
① 当两指针没有相遇时
若左边指针指向的数组元素小于划分标杆K时,左指针向右移动。
若右边指针指向的数组元素小于划分标杆K时,左指针向左移动。
② 当两指针没有相遇并且左指针指向的数组元素大于K,右指针指向的数组元素小于K,那么交换两指针所指元素。并按①挪动指针。
③ 返回左指针对应的数组下标。
AC代码:
class Solution {
public:
/**
* @param nums: The integer array you should partition
* @param k: An integer
* @return: The index after partition
*/
int partitionArray(vector<int> &nums, int k) {
if(nums.size() == 0)
return 0;
int i = 0;
int j = nums.size() - 1;
while(i <= j) {
while(i <=j && nums[i] < k) i++;
while(i <= j && nums[j]>=k) --j;
if(i<=j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++;
j--;
}
}
return i;
}
};
3.主元素Ⅰ
描述:
给定一个整型数组,找出主元素,它在数组中的出现次数严格大于数组元素个数的二分之一。
样例:
样例 1:
输入: [1, 1, 1, 1, 2, 2, 2]
输出: 1
样例 2:
输入: [1, 1, 1, 2, 2, 2, 2]
输出: 2
要求时间复杂度为O(n),空间复杂度为O(1)
方法:
定义一个变量counts并初始化为 0 ,用来记录当前元素出现次数;currentNumber记录正在被比较的元素。只要被比较元素与当前数组元素不相等,那么就把counts减 1 ,当counts为 0 时,把currentNumber替换成现在的数组元素。
AC代码:
class Solution {
public:
/*
* @param nums: a list of integers
* @return: find a majority number
*/
int majorityNumber(vector<int> &nums) {
int counts = 0;//记录元素出现次数
int currentNumber;//记录正在被比较的元素。
for (int i = 0; i < nums.size(); ++i) {
if(counts==0) {
currentNumber = nums[i];
}
if(currentNumber!=nums[i]) {
--counts;
}else{
++counts;
}
}
return currentNumber;
}
};
4.翻转数组
描述:
原地翻转给出的数组 nums
样例:
样例 1:
输入 : nums = [1,2,5]
输出 : [5,2,1]
方法:
以数组中间为界,左右对换元素
AC代码:
class Solution {
public:
/**
* @param nums: a integer array
* @return: nothing
*/
void reverseArray(vector<int> &nums) {
if(nums.size()==0)
return;
int j = nums.size()-1;
for (int i = 0; i < nums.size() / 2 && i!=j; ++i) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
j--;
}
}
};
技巧型
简单难度
1. 尾部的零
描述:
设计一个算法,计算出n阶乘中尾部零的个数。
样例:
样例 1:
输入: 11
输出: 2
样例解释:
11! = 39916800, 结尾的0有2个。
样例 2:
输入: 5
输出: 1
样例解释:
5! = 120, 结尾的0有1个
方法:
看输入的输由多少个5相加组成,有多少个5,就有多少0。
切忌直接算阶乘,然后结尾计算有多少个0,遇到大数的阶乘,long long也存不下,程序会爆掉。
举例:
输入:11
计算:11 / 5 = 2;
输入:105
计算:105 / 5 = 25;
AC代码:
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 number = n / 5;
long count = 0;
while (number > 0){
count += number;
number /= 5;
}
return count;
}
};