2024.3.27
1.移除数组中的元素
给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用
O(1)
额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
双指针(快慢双指针法)
int removeElement(int* nums, int numsSize, int val) {
int src = 0;
int dst = 0;
while(src<numsSize)
{
if(nums[src]==val)
{
src++;
}
else
{
nums[dst] = nums[src];
src++;
dst++;
}
}
return dst;
}
题解:只需要把val元素覆盖即可。把不是val的元素保留下来,是val的元素被不是的元素覆盖。
2.合并两个有序数组
给你两个按 非递减顺序 排列的整数数组
nums1
和nums2
,另有两个整数m
和n
,分别表示nums1
和nums2
中的元素数目。请你 合并
nums2
到nums1
中,使合并后的数组同样按 非递减顺序 排列。注意:最终,合并后数组不应由函数返回,而是存储在数组
nums1
中。为了应对这种情况,nums1
的初始长度为m + n
,其中前m
个元素表示应合并的元素,后n
个元素为0
,应忽略。nums2
的长度为n
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109
思路以及代码如下
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
//第一种方法是将num2中元素先全部插入Num1中,再用排序方法排位递增
//时间复杂度O(N^2)
//有没有一种方法,在合并的同时就进行排序呢?
//要把数据都放在nums1中,就不能从其前向后排序,否则无法将nums2中的数据插入(会导致nums1中数据被覆盖)
int l1 = m - 1;
int l2 = n - 1;
int l3 = m+n-1;
while(l1>=0&&l2>=0)
{
//比较nums1与nums2的最大值,将最大值插入到nums1 的 l3位置
if(nums1[l1]>nums2[l2])
{
nums1[l3--] = nums1[l1--];
}
else
{
nums1[l3--] = nums2[l2--];
}
}
//如果l1小于0,l2仍大于0时,直接将l2中剩余元素全部插入
while(l2>=0)
{
nums1[l3--] = nums2[l2--];
}
//如果l1>=0,l2小于0呢,那说明排序已经完成
}
3.移除链表元素
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点
- 列表中的节点数目在范围
[0, 104]
内1 <= Node.val <= 50
0 <= val <= 50
方法一:建立头节点
typedef struct ListNode ListNode;
ListNode* removeElements(ListNode* head, int val) {
//方法一:循环一个一个删,时间复杂度O(n)
//但是在没有头结点的情况下,河南区判断全部要删除和头指针是NULL\head->val==val的情况
//创建一个头结点
ListNode* pcur = head;
ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
phead->next = head;
head = phead;
ListNode* prev = phead;
while(pcur)
{
if(pcur->val==val)
{
prev->next = pcur->next;
pcur = prev->next;
continue;
}
prev = pcur;
pcur = pcur -> next;
}
return phead->next;
}
方法二:建立新链表
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* newhead = NULL;
struct ListNode* newtail = NULL;
struct ListNode* pcur = head;
while(pcur)
{
if(pcur->val!=val)
{
if(newhead==NULL)
{
newhead = newtail = pcur;
}
else{
newtail->next = pcur;
newtail = newtail->next;
}
}
pcur = pcur->next;
}
if(newtail)
{
newtail->next = NULL;
}
return newhead;
}
4.反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。
- 链表中节点的数目范围是
[0, 5000]
-5000 <= Node.val <= 500
0
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
//链表为空时
if(head==NULL)
{
return head;
}
//创建3个指针发呢别记录前驱节点,当前系欸但,候集解带你,改变原链表指向
//创建三个指针
ListNode* n1,*n2,*n3;
n1 = NULL,n2 = head,n3=head->next;
//遍历原链表,修改指针指向
while(n2)
{
//n2指向
n2->next = n1;
n1 = n2;
n2 =n3;
if(n3)
{
n3 = n3->next;
}
}
return n1;
}
2024.3.30
1.链表的中间节点
给你单链表的头结点
head
,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
- 链表的结点数范围是
[1, 100]
1 <= Node.val <= 100
方法一:记录来链表中元素个数,找到中
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
ListNode* pcur = head;
ListNode* mid = head;
int sum = 0;
int midd = 0;
while(pcur)
{
pcur = pcur ->next;
sum++;
}
midd = sum/2;
for(int i = 0;i<midd;i++)
{
mid = mid->next;
}
return mid;
}
方法二:快慢指针法,l2速度是l1的二倍,当l2走到末尾时,l1刚好走到中间。
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
ListNode *p1,*p2;
p1 = p2 = head;
while(p2&&p2->next)
{
p1 = p1->next;
p2 = p2->next->next;
}
return p1;
}
2.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
方法:创建一个新链表,比较两个原链表,谁小谁插入
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1==NULL)
{
return list2;
}
if(list2==NULL)
{
return list1;
}
ListNode *newhead , *newtail;
newhead = newtail = NULL;
while(list1 && list2)
{
if(list1->val < list2->val)
{
if(newhead == NULL)
{
newhead = newtail = list1;
}
else{
newtail->next = list1;
newtail = list1;
}
list1 = list1->next;
}
else{
if(newhead == NULL)
{
newhead = newtail = list2;
}
else{
newtail->next = list2;
newtail = list2;
}
list2 = list2->next;
}
}
if(list1)
{
newtail->next = list1;//自动连接
}
if(list2)
{
newtail->next = list2;
}
return newhead;
}
3.分割链表
给你一个链表的头节点
head
和一个特定值x
,请你对链表进行分隔,使得所有 小于x
的节点都出现在 大于或等于x
的节点之前。你不需要 保留 每个分区中各节点的初始相对位置。
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
方法一:创建两个新链表,一个保存比x小的元素,另一个存储比x大的元素,最后将两个链表连接起来,可以创建哨兵位也可以不创建,创建的优点在于不用if判断存储大数据链表为空/存储小数据链表为空。
1.不使用哨兵位
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
ListNode* pcur = head;
ListNode *Shead,*Stail;
Shead = Stail = NULL;
ListNode *Lhead,*Ltail;
Lhead = Ltail = NULL;
if(pcur==NULL)
{
return head;
}
while(pcur)
{
if(pcur->val<x)
{
//搞好头
if(Shead==NULL)
{
Shead = Stail = pcur;
}
else{
Stail->next = pcur;
Stail = pcur;
}
}
else
{
//搞好头
if(Lhead==NULL)
{
Lhead = Ltail = pcur;
}
else{
Ltail->next = pcur;
Ltail = pcur;
}
}
pcur = pcur->next;
}
if(Lhead==NULL)
{
return Shead;
}
if(Shead==NULL)
{
return Lhead;
}
Ltail->next = NULL;
Stail->next = Lhead;
return Shead;
}
2.使用哨兵位
//不使用头节点创建一个全新链表会使得我们还要判断Shead==NULL,Lhead==NULL
//使用的化大小链表一定不会是空链表了
ListNode* pcur = head;
if(pcur==NULL)
{
return head;
}
ListNode *Shead,*Stail;
ListNode *Lhead,*Ltail;
Shead = Stail = (ListNode*)malloc(sizeof(ListNode));
Lhead = Ltail = (ListNode*)malloc(sizeof(ListNode));
while(pcur)
{
if(pcur->val<x)
{
Stail->next = pcur;
Stail = pcur;
}else{
Ltail->next = pcur;
Ltail = pcur;
}
pcur = pcur->next;
}
Ltail->next = NULL;
//大小链表首尾相连
Stail->next = Lhead->next;
ListNode *ret = Shead->next;
free(Shead);
free(Lhead);
return ret;
}
4.环形链表的约瑟夫问题
编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。
下一个人继续从 1 开始报数。
n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?
数据范围:1≤n,m≤10000
进阶:空间复杂度 O(1),时间复杂度 )O(n)
法:从1--n创建一个单向循环链表,分别存储1----n,遍历整个链表,每次++count,当count为m时删除链表元素,再将count重新至为1.最后返回pcur->val;
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @param m int整型
* @return int整型
*/
#include <stdlib.h>
typedef struct ListNode ListNode;
ListNode* BuyNode(int x)
{
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode ->val = x;
newNode->next = NULL;
return newNode;
}
ListNode* CreateList(int n)
{
ListNode* phead = BuyNode(1);
ListNode* ptail = phead;
for(int i = 2;i<=n;i++)
{
ptail->next = BuyNode(i);
ptail = ptail->next;
}
//首尾相连
ptail ->next =phead;
return phead;
}
int ysf(int n, int m ) {
int count = 1;
ListNode *phead = CreateList(n);
ListNode *pcur = phead;
ListNode *prev = NULL;
while(pcur->next!=pcur)
{
if(count==m)
{
prev->next = pcur->next;
free(pcur);
pcur = prev->next;
count = 1;
}
else {
{
prev = pcur;
pcur = pcur->next;
count++;
}
}
}
return pcur->val;
}
2024.3.31
1.消失的数字
数组
nums
包含从0
到n
的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
法1:0-n的和减去 循环数组得到所有元素的值,即为缺失的数字。
int missingNumber(int* nums, int numsSize){
int sum = 0;
for(int i = 0;i < numsSize;i++)
{
sum = sum + i - nums[i];
}
return sum+numsSize;
}
法2:(单身狗)使用异或
结论1:两数16进制位数 相异得到1,相同得到0,例如3^4=7.
结论2:0与任何数异或都为任何数,例如0^4=4,0^3=0
结论3:连续异或符合交换律。例如 x = 3^3^4 = 4; x = 3^4^3 = 4; 可以自己去算一下。
那么,先用0异或数组中的所有元素,再异或0-n的所有元素,的大排档结果就是单身狗
int missingNumber(int* nums, int numsSize){
int x = 0;
for(int i = 0;i<numsSize;i++)
{
x = x^nums[i];
x = x^i;
}
x = x^numsSize;
return x;
}
2.轮转数组
给定一个整数数组
nums
,将数组中的元素向右轮转k
个位置,其中k
是非负数。示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右轮转 1 步:[7,1,2,3,4,5,6]
向右轮转 2 步:[6,7,1,2,3,4,5]
向右轮转 3 步:[5,6,7,1,2,3,4]
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
法一:现将数组最后一个元素保存下来,再将所有数据向后移动1位,再将保存的元素放进数组前k位,循环k次,复杂度O(N^2),会超时,不采用。
法二:数组的三段逆置法:先将前n-k个逆置,再将后k个逆置,最后将整体逆置。
注意:最好情况:当k是numsSize的整数倍,相当于不动。
最坏情况:当k%numsSize = numsSize - 1;
void Reverse(int* nums,int left,int right)
{
//二者相等是停下
while(left<right)
{
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
right--;
}
}
void rotate(int* nums, int numsSize, int k) {
k = k%numsSize;
//先将前n-k个逆置
Reverse(nums,0,numsSize-k-1);
//再将后K个逆置
Reverse(nums,numsSize-k,numsSize- 1);
//整体逆置
Reverse(nums,0,numsSize-1);
}
法三:法二比较难想出来。我们可以创建一个新数组(大小为numsSize,LeetCode 支持C99变长数组),将原数组的(n-k,n-1)复制到新数组的(0,k-1),再将原数组的(0,n-k-1)复制到新数组的(k,n-1).最后将新数组的每一个元素赋给旧数组即可(memcpy/for循环)
void rotate(int* nums, int numsSize, int k) {
k = k%numsSize;
int a[numsSize];
int n = numsSize;
memcpy(a,nums+n-k,k*sizeof(int));
memcpy(a+k,nums,sizeof(int)*(n-k));
memcpy(nums,a,sizeof(int)*n);
}