Code 1 : Squares of a Sorted Array
Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.
Example 1
Input: nums = [-4,-1,0,3,10]
Output: [0,1,9,16,100]
Explanation: After squaring, the array becomes [16,1,0,9,100].
After sorting, it becomes [0,1,9,16,100].
Example 2
Input: nums = [-7,-3,2,3,11]
Output: [4,9,9,49,121]
Solution
- 冒泡排序
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for (int i=0;i<nums.size();i++){
nums[i]=(nums[i]*nums[i]);
}
for (int i=0;i<nums.size();i++){
for(int j=0;j<nums.size()-i-1;j++){
if (nums[j]>nums[j+1]){
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
return nums;
}
};
注意冒泡排序的内部循环。
for (int i=0;i<nums.size();i++){
for(int j=0;j<nums.size()-i-1;j++){
if (nums[j]>nums[j+1]){
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
与以下的选择排序作对比。
- 选择排序
vector<int> sortedSquares(vector<int>& nums) {
for (int i=0;i<nums.size();i++){
nums[i]=(nums[i]*nums[i]);
}
for (int i=0;i<nums.size();i++){
for(int j=i+1;j<nums.size();j++){
if (nums[j]<nums[i]){
int temp=nums[j];
nums[j]=nums[i];
nums[i]=temp;
}
}
}
for (int i=0;i<nums.size();i++)
cout<<nums[i]<<' ';
return nums;
}
j遍历i后的全部元素,如果j对应的值比i对应的值要大,就交换i和j。
循环部分:
for (int i=0;i<nums.size();i++){
for(int j=i+1;j<nums.size();j++){
//这里有区别
if (nums[j]<nums[i]){
int temp=nums[j];
nums[j]=nums[i];
nums[i]=temp;
}
}
}
对于这道题,选择排序和冒泡排序都会超时。
- 直接插入法
class Solution {
public:
//直接插入排序
vector<int> sortedSquares(vector<int>& nums) {
int n=nums.size();
if(n==0) return nums;
nums[0]=nums[0]*nums[0]; //改变第一个
for(int i=1;i<n;i++){
int temp=nums[i]*nums[i]; //找到要插入元素的对应值
int j=i;
while(j>0 and temp<=nums[j-1]){
//找到插入位置
nums[j]=nums[j-1]; //插入位置后每个元素往后移一个(可以覆盖掉要插入的元素)
j--;
}
nums[j]=temp; //相应地方的赋值
}
return nums;
}
};
直接插入是在前面的排序已经完成的情况下进行的。插入时定位到插入位置同时将其余元素后移(不用动插入元素后面的)。
- 折半查找
vector<int> sortedSquares(vector<int>& nums) {
if (nums.size()==0) return nums;
nums[0]=nums[0]*nums[0];
for (int i=1;i<nums.size();i++){
int temp=nums[i]*nums[i];
int l=0;
int r=i-1;
while (l<=r){
int mid=(l+r)/2;
if(nums[mid]<=temp){
l=mid+1;
}
else{
r=mid-1;
}
}
for(int j=i;j>r;j--){
nums[j]=nums[j-1];
}
nums[l]=temp;
}
return nums;
}
有点类似于二分查找。
- 双指针
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int i=0,j=nums.size()-1;
vector<int> result(nums.size());
int r=result.size()-1;
while(i<=j){
if(nums[i]*nums[i]<=nums[j]*nums[j]){
result[r]=nums[j]*nums[j];
cout<<result[r]<<endl;
j--;
r--;
}
else{
result[r]=nums[i]*nums[i];
i++;
r--;
}
}
return result;
}
};
本题给出的数组是有序的,需要多考虑的是负数的部分,发现平方后最大值都在两边,中间的绝对值最小。考虑双指针法,使i
指向起始位置,j
指向终止位置。通过移动i和j来将数组从大到小插入result
中
Code 2 : Rotate Array
Given an array, rotate the array to the right by k steps, where k is non-negative.
Example 1
Input: nums = [1,2,3,4,5,6,7], k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]
Example 2
Input: nums = [-1,-100,3,99], k = 2
Output: [3,99,-1,-100]
Explanation:
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]
Solution
- 反转三次
class Solution {
public:
void rotate(vector<int>& nums, int k) {
reverse(nums,0,nums.size()-1);
reverse(nums,0,k%nums.size()-1);
reverse(nums,k%nums.size(),nums.size()-1);
}
void reverse(vector<int>& nums,int start,int end){
int i=start,j=end;
while(i<=j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
i++;
j--;
}
}
};
假设原vector是 1234567
, 要求移动三次, 结果应该是5671234
。
这种方法把1234567
分成了两段,先将全部反转,变为7654321
,然后以三个数为边界分成两段 765
4321
再将其分别反转567
1234
。
其中在旋转函数中用到了双指针。
注意k%nums.size()
来处理k大于nums的长度的问题。
- 使用额外空间
class Solution {
public:
void rotate(vector<int>& nums, int k) {
vector<int> tool(nums.size());
for(int i=0;i<nums.size();i++){
tool[(i+k)%nums.size()]=nums[i];
}
nums.assign(tool.begin(),tool.end());
}
};
这里注意理解tool[(i+k)%nums.size()]=nums[i];
的意思。
cpp中assign()
函数的作用是顺次地把一个 string 对象的部分内容拷贝到另一个 string 对象上。
Code 3 : Move Zeros
Given an integer array nums, move all 0’s to the end of it while maintaining the relative order of the non-zero elements.
Note that you must do this in-place without making a copy of the array.
Example 1
Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]
Example 2
Input: nums = [0]
Output: [0]
Solution
- 双指针(从两端逼近,较复杂)
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int i=0;
int j=nums.size()-1;
while(nums[j]==0 && j>=i){
j--;
}
while(i<j){
if(nums[i]==0){
for(int k=i;k<j;k++){
nums[k]=nums[k+1];
}
nums[j]=0;
j--;
}
else{
i++;
}
}
}
};
- 双指针改进版
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int s=0,f=0;
while(f<nums.size()){
if(nums[f]){
swap(nums[s],nums[f]);
s++;
}
f++;
}
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<' ';
}
}
};
这种方法实际上是一直让较慢的指针停留在0的位置,当快的指针不为零的时候就把二者交换,把0换到后面去。
Code 4 : Reverse String
Write a function that reverses a string. The input string is given as an array of characters s.
Example 1
Input: s = ["h","e","l","l","o"]
Output: ["o","l","l","e","h"]
Example 2
Input: s = ["H","a","n","n","a","h"]
Output: ["h","a","n","n","a","H"]
Solution
- 双指针
class Solution {
public:
void reverseString(vector<char>& s) {
int l=0,r=s.size()-1;
while(l<r){
swap(s[l],s[r]);
l++;
r--;
}
}
};
具体思路在移动数组那一题有交叉的地方。
Code 5
Solution
- 双指针(调用外部函数)
class Solution {
public:
string reverseWords(string s) {
int pt=0;
int start=0;
for(int i=0;i<s.size();i++){
if(s[i]==' '){
reverse(s,start,i-1);
i++;
start=i;
}
if(i==s.size()-1){
reverse(s,start,i);
}
}
return s;
}
void reverse(string &s,int start,int end){
while(start<end){
swap(s[start],s[end]);
start++;
end--;
}
}
};
也是用到了之前的reverse()
的思路,遇到空格的就反转前一个词,最后一个词特殊处理。
- 双指针(不调用外部函数)
class Solution {
public:
string reverseWords(string s) {
int p=0;
while(p<s.size()){
int start=p;
while(s[p]!=' '&&p<s.size()){
p++;
}
int l=start,r=p-1;
while(l<r){
swap(s[l],s[r]);
l++;
r--;
}
while(s[p]==' '&&p<s.size()){
p++;
}
}
return s;
}
};
这种方法较前面那个好一点,不用特意考虑最后一种情况。p在每个单词末尾或者末尾的空格停下然后反转。
- 使用额外的空间
class Solution {
public:
string reverseWords(string s) {
string reS;
int p=0;
while(p<s.size()){
int start=p;
while(s[p]!=' '&&p<s.size()){
p++;
}
for(int i=p-1;i>=start;i--){
reS.push_back(s[i]);
}
while(s[p]==' '&&p<s.size()){
p++;
reS.push_back(' ');
}
}
return reS;
}
};
想法和上一个相同,但反转每个单词的时候不太一样,用到了额外的空间,性能不高。
注意添加空格的时候要在最后一个while
里添加。
Code 6 : Middle of the Linked List
Given the head of a singly linked list, return the middle node of the linked list.
If there are two middle nodes, return the second middle node.
Example 1
Input: head = [1,2,3,4,5]
Output: [3,4,5]
Explanation: The middle node of the list is node 3.
Example 2
Input: head = [1,2,3,4,5,6]
Output: [4,5,6]
Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one.
Solution
- 快慢双指针(自己写的不是很清晰) 可以和下面的方法对比
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* slow=head;
ListNode* fast=head;
while(fast){
if(fast->next) fast=fast->next;
else return slow;
if(fast->next) fast=fast->next;
else return slow->next;
slow=slow->next;
}
return slow;
}
};
- 双指针简洁版
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* slow=head;
ListNode* fast=head;
while(fast && fast.next){
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
}
- 另一种方法是用单指针遍历后返回中间的,比较复杂。
Code 7 : Tow Sum
Given an array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number.
Return the indices of the two numbers (1-indexed) as an integer array answer of size 2, where 1 <= answer[0] < answer[1] <= numbers.length.
The tests are generated such that there is exactly one solution. You may not use the same element twice.
Example 1
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.
Example 2
Input: numbers = [2,3,4], target = 6
Output: [1,3]
Example 3
Input: numbers = [-1,0], target = -1
Output: [1,2]
Solution
- 双指针
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int l=0,r=numbers.size()-1;
while(l<r){
if(numbers[l]+numbers[r]>target) r--;
else if (numbers[l]+numbers[r]<target) l++;
else{
vector<int> a={
l+1,r+1};
return a;
}
}
return vector<int>();
}
};
利用vector是顺序排列的特性,规定左右两个指针,如果比目标结果大了就移动右指针(减小),比目标小就移动左指针(增大)。
Code 8 : Remove Nth Node From End of List
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Example 1
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Example 2
Input: head = [1], n = 1
Output: []
Example 3
Input: head = [1,2], n = 1
Output: [1]
Solution
- 双指针(快慢指针)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
ListNode* first=head;
ListNode* second=dummy;
for(int i=0;i<n;i++){
first=first->next;
}
while(first){
second=second->next;
first=first->next;
}
second->next=second->next->next;
return dummy->next;
}
};
快慢指针相差n个节点,当快节点到尽头的时候,慢节点刚好是要删除的前一个。
一般在处理链表问题时,可以考虑加入哑节点,它的 next
指针指向链表的头节点,这就不需要对头节点进行特殊的判断了。