LeetCode:1、9、29、136、143。

28 篇文章 0 订阅
12 篇文章 0 订阅

写在前面:

LeetCode每周五道题(初阶)系列博客介绍
ps.我为什么要每周做五道题,那当然是为了让这个世界上少一个菜鸡。
目标:每周刷五道题
题目范围:不限
题目构成:4+1(四道简单题目加一道中等题目)
注:后续的题目数量以及难易程度会慢慢调整

一、题目大纲

  1. 两数之和(leetcode 题号:1,难度:简单)
  2. 回文数(leetcode 题号:9,难度:简单)
  3. 删除有序数组中的重复项(leetcode 题号:29,难度:简单)
  4. 只出现一次的数字 (leetcode 题号:136, 难度:简单)
  5. 重排链表 (leetcode 题号:143, 难度:中等)

1.1两数之和 (leetcode 题号:1,难度:简单)

1.1.1 题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

1.1.2 解法:暴力遍历循环

首先在不考虑时间和空间复杂度的情况下,可以直接使用双循环遍历的方法,从数组的下标0开始,向后遍历,判断之和等于target时将两个元素的下标返回即可。
代码示例:

class Solution {
public:
	vector<int> twoSum(vector<int>& nums, int target) {
		int i = 0;
		int len = nums.size();
		vector<int> vctResult;
		for (i = 0; i < len; i++)//双层遍历即可,当前元素自身不需要和自己比较
		{
			int j = 0;
			if (i + 1 == len)
			{
				break;
			}
			for (j = i + 1; j < len; j++)
			{
				if ((nums[i] + nums[j]) == target)
				{
					vctResult.push_back(i);
					vctResult.push_back(j);
				}
			}
		}
		return vctResult;
	}
};

执行结果:
在这里插入图片描述

1.1.3 更好一点的方法

上面的暴力法相当于将所有的排列组合列出来了,因此时间复杂度非常高,其实我们只需要每次去判断 target - nums[i] 存不存在即可,存在则将该组合的下标输出即可
代码示例:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int, int> mapTemp;
        vector<int> vctResult;
        for(int i = 0; i < nums.size();i++)
        {
            if(mapTemp.find(target - nums[i]) != mapTemp.end())//查找目标值是否存在
            {
                vctResult.push_back(mapTemp[target-nums[i]]);
                vctResult.push_back(i);
                break;
            }
            else
            {
                mapTemp.insert(make_pair(nums[i], i));//存入该数和下标
            }
        }
        return vctResult;
    }
};

运行结果:
在这里插入图片描述
空间上可以在原数组进行修改:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int, int> mapTemp;
        for(int i = 0; i < nums.size();i++)
        {
            if(mapTemp.find(target - nums[i]) != mapTemp.end())//查找目标值是否存在
            {
                nums[0] = mapTemp[target-nums[i]];//直接在原数组上修改
                nums[1] = i;
                nums.resize(2);
                break;
            }
            else
            {
                mapTemp.insert(make_pair(nums[i], i));//存入该数和下标
            }
        }
        return nums;
    }
};

至于结果为什么没有空间优化,那就不得而知了
在这里插入图片描述

2.回文数(leetcode 题号:9,难度:简单)

2.1 题目描述

给你一个整数 x ,如果 x 是一个回文整数,返回 true否则,返回 false
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

示例 4:

输入:x = -101
输出:false

2.2 解法:先将整数的 [个十百千…位] 上的数字取出来,存入vector中然后进行前后逐个比较即可

代码示例:

class Solution{
public:
	bool isPalindrome(int x) {
		vector<int> v;
		if (x < 0)//负数直接返回False
		{
			return false;
		}
		if ((x>=0)&&(x<10))//个位数直接返回true
		{
			return true;
		}
		while (x > 0)//取出来每个位置的数字
		{
			int temp = x % 10;
			x /= 10;
			v.push_back(temp);
		}
		int k = 0;
		int end = v.size()-1;
		for (k = 0; k < v.size()/2; k++)//首尾逐个进行比较
		{
			if (v[k] != v[end])
			{
				return false;
			}
			end--;
		}
		return true;
	}
};

执行结果:
在这里插入图片描述

3.删除有序数组中的重复项(leetcode 题号:29,难度:简单)

3.1 题目描述

给你一个有序数组 nums(升序) ,请你 原地 删除重复出现的元素使 每个元素 只出现一次 ,返回删除后数组的新长度

题目解析,这里是传引用的方式进行传参,意思是需要在原数组上进行修改,删除多余的元素,返回一个数组的长度,leetcode后台会根据你传的数组的长度对数组进行取元素,例如长度为1,就选取数组中的第一个元素(下标为0),然后用测试用例的结果进行比对,判断是否通过测试用例。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

不允许申请新的空间,只可以在原数组修改

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

3.2 解法:使用双下标循环法,将不同的元素依次放在数组的[0,1,2,…]下标处的位置即可。

在这里插入图片描述
代码示例:

class Solution {
public:
	int removeDuplicates(vector<int>& nums) {
		int k = 0;
		int temp = k + 1;
		int count = 1;
		if (nums.size()==1||nums.size()==0)//特殊值处理
		{
			return nums.size();
		}
		while (k < nums.size())
		{
			while (nums[k] == nums[temp])
			{
				if (temp == nums.size() - 1)//当已经走到最后一个元素直接返回
				{
					return count;
				}
				if (temp + 1 != nums.size())//注意越界
				{
					temp++;
				}
			}
			if (nums[k] != nums[temp])//找到不重复的元素
			{
				nums[k + 1] = nums[temp];//赋值
				count++;//计数
				if (temp == nums.size() - 1)
				{
					return count;
				}
				else
				{
					k++;
                    temp++;//下标继续向后移动
				}
			}
		}
        return count;
	}
};

执行结果:
在这里插入图片描述
注意的点:
1.首先要注意数组的越界访问问题,因为有两个数组下标,且两个下标是交互的进行跳跃的,因此很容易出现越界的情况.
2.还要注意以下边界值的情况,考虑数组为空的情况,且对一些特殊的数组,例如只有一个元素的数组直接返回1即可。
在这里插入图片描述

4.只出现一次的数字 (leetcode 题号:136, 难度:简单)

4.1 题目描述

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素

说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

4.2 解法:按位异或

因为只有一个元素是不重复的,其他的元素都是两个两个相同的,根据按位异或的知识可以知道,两个相同的元素按位异或为0,而任何一个数与0异或都是这个数本身。
代码示例:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int i = 0;
		for (i = 1; i < nums.size(); i++)
		{
			nums[0] ^= nums[i];
		}
		return nums[0];
    }
};

执行结果:
在这里插入图片描述

5.重排链表 (leetcode 题号:143, 难度:中等)

5.1题目描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln-1 → Ln
请将其重新排列后变为:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换

示例1:

在这里插入图片描述
输入: head = [1,2,3,4]
输出: [1,4,2,3]

示例2:

在这里插入图片描述
输入: head = [1,2,3,4,5]
输出: [1,5,2,4,3]

5.2 解法:

首先需要明白这个是要进行结点插入,将倒数第一个结点插入到第一个结点之后,将倒数第二个结点插入到第二个结点的后面(更准确的来说是第三个结点的后面,因为前面已经插入了一个结点)

那么整体的思路就是:

1.依次找到最后一个结点,将这个结点保存下来,用于后面的插入。
在这里插入图片描述2.将结点在正确的结点后面进行插入
在这里插入图片描述
代码示例:

class Solution {
public:
    void reorderList(ListNode* head) {
        if(head->next == nullptr||head->next->next==nullptr )//对只有一个结点或者两个结点的情况进行处理
        {
            //不做任何处理
        }
        else
        {
        ListNode* curNode = head;//记录当前节点的位置
        while((curNode->next!=nullptr)&&(curNode->next->next)!=nullptr)//插入结点的结束条件并且判空
        {
                ListNode* pNext = head;
                ListNode* pLast = nullptr;
                while(pNext->next->next!=nullptr)//找到尾结点的前一个结点
                {
                    pNext = pNext->next;
                }
                pLast = pNext->next;//最后一个节点
                pNext->next = nullptr;//将倒数第二个结点的next置为nullptr
                ListNode* tempNode = curNode->next;//保存插入节点的下一个结点
                curNode->next = pLast;//插入并链接前后结点
                pLast->next = tempNode;
                curNode = tempNode;//设置下一次插入的位置(后插入)
        }
        }
    }
};

执行结果:
在这里插入图片描述
注意的点:
1.首先这道题需要理清解题的思路,即插入的结点是什么,插入的位置是什么,什么时候结束插入。
2.注意边界值的判断,并且注意尾结点的next的置空,对结点的判空,否则就会出现崩溃,非法访问,导致不能通过所有的测试用例。

本地测试代码:

#include<vector>
#include<iostream>
using namespace std;
//1.两数之和(leetcode 题号:1)
class Solution1 {  
public:
	vector<int> twoSum(vector<int>& nums, int target) {
		int i = 0;
		int len = nums.size();
		vector<int> vctResult;
		for (i = 0; i < len; i++)//暴力双循环
		{
			int temp = 0;
			int j = 0;
			if (i + 1 == len)
			{
				break;
			}
			for (j = i + 1; j < len; j++)
			{	
				if ((nums[i] + nums[j]) == target)
				{
					vctResult.push_back(i);
					vctResult.push_back(j);
				}
			}
		}
		return vctResult;
	}
};
//2.回文数(leetcode 题号:9)
class Solution2 {
public:
	bool isPalindrome(int x) {
		vector<int> v;
		if (x < 0)//负数直接返回false
		{
			return false;
		}
		if ((x >= 0) && (x < 10))//个位数直接返回true
		{
			return true;
		}
		while (x > 0)
		{
			int temp = x % 10;//先取个位数,再取百位,千位....依次类推
			x /= 10;
			v.push_back(temp);
		}
		int k = 0;
		int end = v.size()-1;
		for (k = 0; k < v.size()/2; k++)//从头部和尾部依次开始比较
		{
			if (v[k] != v[end])
			{
				return false;
			}
			end--;
		}
		//int j = 0;
		//for (j = 0; j < v.size(); j++)
		//{
		//	cout << v[j] << " " << endl;
		//}
		return true;
	}
};
void PrintResult(vector<int> vct)//打印数组元素函数
{
	int j = 0;
	for (j = 0; j < vct.size(); j++)
	{
		cout << vct[j] << " " << endl;
	}
}
//3.删除有序数组中的重复项(leetcode 题号:29)
class Solution3 {
public:
	int removeDuplicates(vector<int>& nums) {
		int k = 0;
		int temp = k + 1;//这个变量是一直存在的 而不是每次循环位置被重新改变
		int count = 1;
		if (nums.size()==1||nums.size()==0)//判断边界值或者只有一个元素的情况
		{
			return nums.size();
		}
		while (k < nums.size())
		{
			while (nums[k] == nums[temp])
			{
				if (temp == nums.size() - 1)//遍历到了最后一个元素 
				{
					return count;
				}
				if (temp + 1 != nums.size())
				{
					temp++;//遇到相同元素位置++
				}
			}
			if (nums[k] != nums[temp])//找到不重复的元素
			{
				nums[k + 1] = nums[temp];//赋值
				temp++;
				count++;//计数器++
				if (temp == nums.size() - 1)
				{
					return count;
				}
				else
				{
					k++;//下一个开始比较的位置
				}
			}
		}
		return count;
	}
};
//4.只出现一次的数字 (leetcode 题号 136)
class Solution4 {
public:
	int singleNumber(vector<int>& nums) {
		int i = 0;
		for (i = 1; i < nums.size(); i++)
		{
			nums[0] ^= nums[i];
		}
		return nums[0];
	}
};
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(1);
	//v.push_back(1);
	v.push_back(2);
	v.push_back(2);
	//v.push_back(2);
	v.push_back(3);
	//Solution1 s;
	//s.twoSum(v, 6);
	//PrintResult(s.twoSum(v, 6));
	//Solution2 s2;
	//s2.isPalindrome(121);
	//Solution3 s3;
	//cout << s3.removeDuplicates(v) << endl;
	Solution4 s4;
	cout<<s4.singleNumber(v);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值