数据结构 顺序表/ 链表oj 曲四
Posted 一个正直的男孩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 顺序表/ 链表oj 曲四相关的知识,希望对你有一定的参考价值。
leetcode (27)移除元素
- 基于顺序表
🙉:题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间
,你必须仅使用 O(1) 额外空间并 原地修改输入数组
。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
🙊:案例
🐒:思路
快慢针:
- 创建dest和sur指针
- sur去找val值如果不是就吧sur赋值给dest
- sur等于val则一直迭代
图解:
🐵:代码
int removeElement(int* nums, int numsSize, int val)
{
int strc =0;
int dest=0;
while(strc<numsSize)
{
if(nums[strc]==val)
{
strc++;
}
else
{
nums[dest]=nums[strc];
dest++;
strc++;
}
}
return dest;
}
leetcode (26) 删除数组重复项
🙉:题目
给你一个有序数组 nums ,请你 原地
删除重复出现的元素
,使每个元素 只出现一次 ,返回删除后数组的新长度
。
不要使用额外的数组空间
,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成
🙊:案例
🐒:思路
快慢指针
- 创建dest和sur指针
- 不同sur与dest是否相同,不同赋值dest+1(不是dest+1那么
头元素就被覆盖了
) - 相同sur迭代
图解
🐵:代码
int removeDuplicates(int* nums, int numsSize)
{
int strc =0;
int dest=0;
while(strc<numsSize)
{
if(nums[strc]==nums[dest])
{
strc++;
}
else
{
dest++;
nums[dest]=nums[strc];
strc++;
}
}
if(numsSize!=0)
{
return dest+1;
}
return 0;
}
注意他可能传的是空数组,如果为空那么返回dest+1打印就越界了
leetcode (88) 合并俩个有序数组
🙉:题目
给你
两个有序整数数组
nums1 和 nums2,请你将 nums2合并到 nums1
中,使 nums1 成为一个有序数组。
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。
🙊:案例
🐒:思路
思路一(暴力解法):
- 直接把数组二插入数组一后面
- qsort排序
图解
🐵:代码
int compar(const void *p1,const void*p2)
{
return (*(int*)p1)-(*(int*)p2);
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int a=m;
for(int i =0;i<n;i++)
{
nums1[a]=nums2[i];
a++;
}
qsort(nums1,n+m,sizeof(int),compar);
}
思路二
- 都指向需要比最后的元素
- 比较俩数组大小,大的放到一数组最后一个位置
- 依次迭代迭代
图解
🐵:代码
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int i1=m-1,i2=n-1;
int tail=m+n-1;
while(i1>=0&&i2>=0)
{
if(nums1[i1]>nums2[i2])
nums1[tail--]=nums1[i1--];
else
nums1[tail--]=nums2[i2--];
}
while(i2>=0)
{
nums1[tail--]=nums2[i2--];
}
}
leetcode (203) 移除链表元素
🙉:题目
给你一个链表的头节点 head 和一个整数 val ,请你
删除链表中所有满足 Node.val == val 的节点
,并返回 新的头节点 。
🙊:案例
🐒:思路
思路一
- 把指针给cur
- cur找cur下一个节点的值是否和val一样
- 一样释放,不一样迭代
- 注意(头的值也可能等于val),(且可能是空链表)
图解
🐵:代码
struct ListNode* removeElements(struct ListNode* head, int val)//返回指针接口
{
if(head==NULL)
{
return NULL;
}
struct ListNode* cur=head;
struct ListNode* hind=head;
while(cur->next)
{
if(cur->next->val==val)
{
struct ListNode*next=cur->next->next;
free(cur->next);
cur->next=next;
}
else
{
cur=cur->next;
}
}
if(head->val==val)
{
struct ListNode*next=head->next;
free(head);
head=next;
}
return head;
}
思路二
- 三个指针,cur,list,tail
- cur找相等val的值
- list为这个新链表的头
- tail为cur的上一个节点
- 找到free,迭代
- 没找到吧cur给tail,迭代
如图所示
🐵:代码
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode*cur=head;
struct ListNode*list=NULL; struct ListNode*tail=NULL;
while(cur)
{
struct ListNode*next=cur->next;
if(cur->val==val)
{
free(cur);
}
else
{
if(list==NULL)
{
list=cur;
tail=list;
}
else
{
tail->next=cur;
}
}
cur=next;
}
return list;
}
leetcode (206) 反转链表
🙉:题目
给你单链表的头节点 head ,请你
反转链表
,并返回反转后的链表
🙊:案例
🐒:思路
思路一
创建节点法:
- 在老节点后面创建新节点(应为头插会
改变原来链表
的顺序死循环
) - 剥离开辟的节点插入新链表
- 链接老链表与节点
- 迭代(这种方法不太推荐容易搞混且效率不是很高)
🐵:代码
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* newlist=NULL;
struct ListNode*cur=head;
while(cur)//在老节点后插入一个新节点
{
struct ListNode*next=cur->next;
struct ListNode*newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
newnode->next=next;
newnode->val=cur->val;
cur->next=newnode;
cur=cur->next->next;
}
cur=head;
while(cur)//剥离新节点并插入到新链表上
{
struct ListNode* curnext=cur->next;
cur=curnext->next;
curnext->next=NULL;
if(newlist==NULL)
{
newlist=curnext;
}
else
{
struct ListNode*oldhead =newlist;
newlist=curnext;
newlist->next=oldhead;
}
}
return newlist;
}
思路二
翻转链表法:
- 反转链表链接顺序
- 头变尾,尾变头
- 三个指针完成置换
如图所示:
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)
{
return NULL;
}
struct ListNode*n1=NULL;
struct ListNode*n2=head;//头
struct ListNode*n3=head->next;//头后面一个节点
while(n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3!=NULL)
n3=n3->next;
}
return n1;
}
牛客 链表倒数第k个节点
🙉:题目
输入一个链表,输出该链表中倒数第k个结点。
其实就是从后往前数
第k个节点
🙊:案例
🐒:思路
快慢指针
- 快指针先走k步(题比较阴间:
给你一个空链表
) - 然后慢指针在和块指针一起走
- 代码很简单,但是想法很难
- (这个方法一般人可想不到,幸好我不是一般人,我是二般人,好吧我也是一般人,大佬思路。多提一嘴,孰能生巧(
勤加练习
+复盘
)+多见风浪(多刷题)
,以后碰到hr都不慌)
图解:
🐵:代码
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
ListNode* fast=pListHead;
ListNode* slow=pListHead;
while(k--)
{
if(fast==NULL)
{
return NULL;
}
fast=fast->next;
}
while(fast)
{
fast=fast->next;
slow=slow->next;
}
return slow;
}
leetcode (879) 链表的中间节点
🙉:题目
给定一个头结点为 head 的非空单链表,
返回链表的中间结点
,如果有两个中间结点,则返回第二个中间结点
。
🙊:案例
🐒:思路
快慢指针
- fast走俩步
- slow走一步
- 这样就可以取到中间节点
- 注意链表可能会有
基数
个or偶数
个节点,所以要迭代的话条件要满足
图解:
🐵:代码
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode*fast=head;
struct ListNode*slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
leetcode (21)合并连个有序链表
🙉:题目
将两个
升序链表
合并为一个新的升序
链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
🙊:案例
🐒:思路
思路一:
一一对比法
- 俩链表从前往后一一对比
- 小的尾插到新链表
🐵:代码
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
if(l1==NULL&&l2==NULL)
{
return NULL;
}
struct ListNode*newlist=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode*cur=newlist;
while(l1&&l2)
{
if(l1->val<l2->val)
{
cur->next=l1;
l1=l1->next;
}
else
{
cur->next=l2;
l2=l2->next;
}
cur=cur->next;
}
if(l1==NULL)
cur->next=l2;
if(l2==NULL)
cur->next=l1;
struct ListNode*head=newlist->next;
free(newlist);
return head;
}
这里用到了
带头单链表
,俗称哨兵位,(其实也可以不用)
思路二:
直插法
3. 判断俩个连表的头节点小的付给新链表
4. 一一对比
5. 上面方法的另一种
🐵:代码
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
struct ListNode*n1=l1;
struct ListNode*n2=l2;
struct ListNode*newhead=NULL;
struct ListNode*tail=NULL;
if(n1->val < n2->val)
{
newhead=tail=n1;
n1=n1->next;
}
else
{
newhead=tail=n2;
n2=n2->next;
}
while(n1&&n2)
{
if(n1->val<n2->val)
{
tail->next=n1;
tail=n1;
n1=n1->next;
}
else
{
tail->next=n2;
tail=n2;
n2=n2->next;
}
}
if(n1)
tail->next=n1;
if(n2)
tail->next=n2;
return newhead;
思路三:
直插法2
- 判断俩个连表的头节点小的付给新链表
- 一一直接插入到练表中更改他们的链接方式(这个就留给你去写吧)
牛客 (综合题) 链表的回文结构
🙉:题目
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
🙊:案例
其实就是这个链表是不是中心对称
🐒:思路
快慢指针,翻转链表,比大小
- 找到中间值(快慢指针)
- 从中间值开始反转成为一个新的链表(翻转链表)
- 一一比大小
- 👆有动图详解
🐵:代码
class PalindromeList {
public:
ListNode* listmiddle(ListNode* A)
{
ListNode*fast=A;
ListNode*slow=A;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
ListNode* reverse(ListNode* middle)
{
ListNode* cur=middle;
ListNode*tail=NULL;
while(cur)
{
ListNode*next=cur->next;
cur->next=tail;
tail=cur;
cur=next;
}
return middle;
}
bool chkPalindrome(ListNode* A)
{
ListNode*middle=listmiddle(A);
ListNode*B=reverse(middle);
while(A&&B)
{
if(A->val!=B->val)
{
return false;
}
A=A->next;
B=B->next;
}
return true;
}
};
leetcode (160) 相交链表
🙉:题目
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表
相交的起始节点
。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1 开始相交:
🙊:案例
🐒:思路
快慢指针
- 先比较俩个链表的长度并求出差值
- 快的先走差值步
- 然后在一起走,一一比较(比较的是
地址
)
图解:
🐵:代码
int size(struct ListNode *head)
{
int count=0;
while(head)
{
++count;
head=head->next;
}
return count;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
int countA=size(headA);
int countB=size(headB);
//你不知道那个大其实也可以比较count,这里就是少写了一个else
struct ListNode*fast=headA;
struct ListNode*slow=headB;
if(countA<countB)
{
fast=headB;
slow=headA;
}
int foot=abs(countA-countB);
while(foot--)
{
fast=fast->next;
}
while(fast&&slow)
{
if(fast==slow)
{
return fast;
}
fast=fast->next;
slow=以上是关于数据结构 顺序表/ 链表oj 曲四的主要内容,如果未能解决你的问题,请参考以下文章