一些数据结构经典习题
part 2
链表
1.给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev=NULL;
struct ListNode* cur=head;
while(cur)
{
if(cur->val==val)
{
if(cur==head)
{
head=cur->next;
free(cur);
cur=head;
}
else
{
prev->next=cur->next;
free(cur);
cur=prev->next;
}
}
else
{
prev=cur;
cur=cur->next;
}
}
return head;
}
此题是对链表删除功能的一个应用,要考虑到特殊情况头删。
做法二
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
struct ListNode
{
int val;
struct ListNode* next;
};
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* tail = NULL;
struct ListNode* cur = head;
//哨兵位
head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
tail->next = NULL;//防止野指针
while (cur)
{
if (cur->val == val)
{
struct ListNode* del = cur;
cur = cur->next;
free(del);
}
else
{
tail->next = cur;
tail = tail->next;
cur = cur->next;
}
}
tail->next = NULL;
struct ListNode* del = head;
head = head->next;
free(del);
return head;
}
int main()
{
struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode));
assert(n1);
struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode));
assert(n2);
struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode));
assert(n3);
struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode));
assert(n4);
n1->val = 7;
n2->val = 7;
n3->val = 8;
n4->val = 7;
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
struct ListNode* head = removeElements(n1, 7);
return 0;
}
反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表
方法一:头插法
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* newhead=NULL;
struct ListNode* cur=head;
while(cur)
{
struct ListNode* next=cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
}
方法二:改变指向
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)//防止n2->next出现野指针
return NULL;
struct ListNode *n1,*n2,*n3;
n1=NULL;
n2=head;
n3=n2->next;
while(n2)
{
//改指向
n2->next=n1;
//迭代,改值
n1=n2;
n2=n3;
//判断是否结束
if(n3)
n3=n3->next;
}
return n1;
}
链表的中间的结点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode * slow,*fast;//*不要掉了
fast=slow=head;//顺序不能变
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;//一次走两格
}
return slow;
}
分两种情况,奇数个和偶数个
链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k )
{
struct ListNode *fast,*slow;
slow=fast=pListHead;
//fast提前走k步
while(k--)
{
//防止还没走到k步,链表就结束了
if(fast==NULL)
return NULL;
fast=fast->next;
}
while(fast)
{
fast=fast->next;
slow=slow->next;
}
return slow;
}
此题使用双指针,快慢指针,注意链表长度和k的关系
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
解法一:
不带哨兵位头结点
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
struct ListNode* head,*tail;
head=tail=NULL;
while(list1&&list2)
{
if(list1->val<list2->val)
{
if(tail==NULL)
{
head=tail=list1;
}
else
{
tail->next=list1;
tail=tail->next;
}
list1=list1->next;
}
else
{
if(tail==NULL)
{
head=tail=list2;
}
else
{
tail->next=list2;
tail=tail->next;
}
list2=list2->next;
}
}
if(list1)
tail->next=list1;
if(list2)
tail->next=list2;
return head;
}
首先判断两个链表是否为空,为空返回另外一个链表
不为空则进入循环,从头判断链表的值大小,其中要判断尾指针是否为空,为空相当于头插链表。
最后,当一个链表为空,直接将另一个链表剩余值返回到尾指针。
带哨兵位头结点解法
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
struct ListNode* head,*tail;
head=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
tail->next=NULL;
while(list1&&list2)
{
if(list1->val<list2->val)
{
tail->next=list1;
tail=tail->next;
list1=list1->next;
}
else
{
tail->next=list2;
tail=tail->next;
list2=list2->next;
}
}
if(list1)
tail->next=list1;
if(list2)
tail->next=list2;
struct ListNode* list=head->next;
free(head);
return list;
}
链表分割
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
此题使用了哨兵位头结点
class Partition {
public:
ListNode* partition(ListNode* pHead, int x)
{
struct ListNode* lesshead,*lesstail,*greaterhead,*greatertail;
lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
lesstail->next=NULL;
greatertail->next=NULL;
struct ListNode* cur=pHead;
while(cur)
{
if(cur->val<x)
{
lesstail->next=cur;
lesstail=lesstail->next;
}
else
{
greatertail->next=cur;
greatertail=greatertail->next;
}
cur=cur->next;
}
lesstail->next=greaterhead->next;
greatertail->next=NULL;
struct ListNode* list=lesshead->next;
free(lesshead);
free(greaterhead);
return list;
}
}
链表的回文结构
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
struct ListNode* middleNode(ListNode* head)
{
struct ListNode * fast,*slow;
slow=fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
struct ListNode* reverseList(ListNode* head)
{
if(head==NULL)
return NULL;
struct ListNode *n1,*n2,*n3;
n1=NULL:
n2=head;
n3=n2->next;
while(n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
n3=n3->next;
}
return n1;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A)
{
struct ListNode* mid=middleNode(A);
struct ListNode* rhead=reverseList(mid);
while(A&&rhead)
{
if(a->val!=rhead->val)
return false;
else
{
A=A->next;
rhead=rhead->next;
}
}
return true;
}
};
此题思路是:先找到中间结点,再将后半部分反转,前部分和后半部分进行比较,相同返回true,不同返回false.
相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
struct ListNode* curA=headA,*curB=headB;
int lenA=1,lenB=1;
while(curA->next)
{
curA=curA->next;
lenA++;
}
while(curB->next)
{
curB=curB->next;
lenB++;
}
if(curA!=curB)
{
return NULL;
}
else
{
struct ListNode* longlist=headA,*shortlist=headB;
if(lenA<lenB)
{
longlist=headB;
shortlist=headA;
}
int gap=abs(lenA-lenB);
while(gap--)
{
longlist=longlist->next;
}
while(shortlist!=longlist)
{
shortlist=shortlist->next;
longlist=longlist->next;
}
return shortlist;
}
}
基本思路是:先求出两个链表的长度,同时如果两个链表不相等则说明不相交,返回NULL。让长的链表先走差距步,当长链表等于断链表时,返回短链表此时就是相交结点。