画解数据结构刷题1-3单向链表

链表旋转

第一题

struct ListNode* rotateRight(struct ListNode* head, int k){
if(k==0||head==NULL||head->next==NULL)
{
    return head;
}
struct ListNode *vtx=head;
int len=1;
while(vtx->next){
vtx=vtx->next;
len++;
}
vtx->next=head;
k=k%len;
int add=len-k;//循环次数
while(add--)
{
    vtx=vtx->next;
}
struct ListNode*ret=vtx->next;
vtx->next=NULL;
return ret;
}

 思路:要输出移动过的链表,先计算len,并让vtx指向最后一个结点,

再将链表首尾相连,再按照题目意思找出适合的循环次数,使vtx遍历到移动后的首元素的前结点,

再输出以前结点的后结点为首元素的链表,将最后一个结点(即前结点)的next断掉,输出

第二题

 

 

 注意:如果按第二个 写,cur之前有联系,但最后输出的是pre,没有链表产生,所以每次要新构建一个strcut为cur->next,让其与pre产生链表(每次while时将cur给p),cur=next就等于原先的cur=cur->nect,但是这里next已经变了

链表相交

 

 

太巧妙了这双指针,简单学习一下链表双指针的写法

链表中的双指针是创建一个新结点,并让vtx=vtx->next,只有两个相等时才输出

链表归并

第一题

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode* newlist;
newlist=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode*tail=newlist;//tail指向newlist的头结点,所以返回newlist->next
struct ListNode*p1=list1;//指向两表的头结点
struct ListNode*p2=list2;
while(p1!=NULL&&p2!=NULL)
{
    if(p1->val<=p2->val)
    {
tail->next=p1;
tail=p1;//list1不会被清空
p1=p1->next;
    }
    else{
        tail->next=p2;
        tail=p2;
        p2=p2->next;
    }
}
if(!p1)
       { tail-> next = p2 ; }
    else if(!p2)
        { tail-> next = p1; } 
return newlist->next;
}

思路:创建一个新结点并为其申请空间,初始化指针指向新结点(新链表),所给2链表的头结点,比较两指针大小,并使用尾插法和tail指针插入元素,最后如果还有剩余,就把他都插入新list

1.将l1赋值给cur

2.为cur->next申请空间并把curnext给cur

3.l1=l1->next

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode*newlist=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode*tail=newlist;//指向newlist的头节点
    struct ListNode*head=newlist;
    if(list1==NULL&&list2==NULL)
    {
        return NULL;
    }
    if(list1==NULL&&list2!=NULL)
    {
        return list2;
    }
    if(list1!=NULL&&list2==NULL)
    {
        return list1;
    }
    while(list1&&list2)
    {
        if(list1->val<list2->val)
        {
tail->next=list1;
tail=list1;
list1=list1->next;
       }
       else{
tail->next=list2;
tail=list2;
list2=list2->next;
       }
    }
    tail->next=list1?list1:list2;
    return head->next;
}

或者初始化头和尾结点,不断利用尾结点插入元素,最后输出head->next

第二题

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode*newlist=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode*tail=newlist;//指向newlist的头节点
    struct ListNode*head=newlist;
    if(list1==NULL&&list2==NULL)
    {
        return NULL;
    }
    if(list1==NULL&&list2!=NULL)
    {
        return list2;
    }
    if(list1!=NULL&&list2==NULL)
    {
        return list1;
    }
    while(list1&&list2)
    {
        if(list1->val<=list2->val)
        {
tail->next=list1;
tail=list1;
list1=list1->next;
       }
       else{
tail->next=list2;
tail=list2;
list2=list2->next;
       }
    }
    tail->next=list1?list1:list2;
    return head->next;
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize){
    for(int i=1;i<listsSize;i++)//两两合并
        lists[0]=mergeTwoLists(lists[0],lists[i]);
    return lists[0];
}

思路:利用将2个升序链表合并的代码,for循环将list[0]与1到n的所有list进行合并

注意:

在2个链表合并的代码中,注意是为一个结点申请空间,然后初始化指针指向他

int FindMin(struct ListNode** List, int listsSize){
    int index=0, min;
    for(int i=0; i<listsSize; i++){//判断指针是否为0
        if(List[i]!=NULL){
            min = List[i]->val;
            break;
        }
    }
    for(int i=0;i<listsSize;i++){
        if(List[i] == NULL)
            continue;
        else if(List[i]->val<=min){
            min = List[i]->val;
            index = i;
        }
    }
    return index;
}

struct ListNode* mergeKLists(struct ListNode** lists, int listsSize){
    int index;
    if(listsSize==0)
    {
        return NULL;
    }
   struct ListNode*Ar[listsSize];
   struct ListNode*ans=(struct ListNode*)malloc(sizeof(struct ListNode));
   struct ListNode*tail=ans;
   struct ListNode*head=ans;
   for(int i=0;i<listsSize;i++)
   {
       Ar[i]=lists[i];
   }
   while(1)
   {
       index=FindMin(Ar,listsSize);
if(Ar[index]==NULL)
{
    break;
}
tail->next=Ar[index];
tail=Ar[index];
Ar[index]=Ar[index]->next;
   }
   return head->next;
}

定义一个在二维数组(存放头结点)中寻找最小值index的函数

{
如果全为null,就要返回0(指向null),循环比较lists[i],更新min和i

}

为一个头结点申请空间,初始化head和tail指针,初始化一个数组ar,存放lists的头结点,

在while(1)里

不断找ar中最小的index,并用尾插法插入,并将使用到的index结点指向其后继结点

第三题

void reorderList(struct ListNode* head){
struct ListNode*cur=head;
struct ListNode*vec[40001];
int i=0;
while(cur)
{
vec[i]=cur;
cur=cur->next;
i++;
}
int p=0;
int q=i-1;
while(p<q)
{
    vec[p]->next=vec[q];
    p++;
    if(p==q)//偶数的时候有用,不然还会执行到下面的语句
    {
        break;
    }
    vec[q]->next=vec[p];
    q--;
}
vec[p]->next=NULL;
}

思路:需要先插第一个,再插最后一个,再插第二个,而单向链表的缺点就是每次查找都要遍历,所以可以将他存入线性表(即数组)中去,每次存只需用vec[1]或vec[n-1]等等

所以,先创建一个数组,将所有结点存入

再定义头尾指针,循环插入,中间的if与奇偶元素不同情况有关

最后将最晚插入的结点的next=NULL

第四题

 

struct ListNode* mergeInBetween(struct ListNode* list1, int a, int b, struct ListNode* list2){
struct ListNode*cur=list1;
struct ListNode*head=list1;
struct ListNode*pa;
struct ListNode*pb;
int i=0;//记录下标
while(cur)
{
    if(i==(a-1))
    {
        pa=cur;
    }
    if(i==(b+1))
    {
        pb=cur;
    }
    cur=cur->next;
    i++;
}
pa->next=list2;
struct ListNode*vtx=list2;
while(vtx->next)
{
    vtx=vtx->next;
}
vtx->next=pb;
return head;
}

思路:重点就是拿到list1中a-1和b+1位置的指针(结点),所以先遍历,并设定坐标i,cur每迭代一次就++,就能拿到位置为a-1和b+1的指针,然后将l2插到a-1,l2的末尾插入b+1即可

链表排序

 

bool sort(const struct ListNode **a,const struct ListNode **b){
    struct ListNode *p = *a, *q = *b;
    return p->val > q->val;
}
struct ListNode* sortList(struct ListNode* head){
    if(!head) return head;
struct ListNode*ret[50001];
int i=0;
while(head)
{
    ret[i++]=head;
    head=head->next;
}
qsort(ret,i,sizeof(struct ListNode*),sort);
for(int j=0;j<i-1;++j)
{
    ret[j]->next=ret[j+1];
}
ret[i-1]->next=NULL;
return ret[0];
}

先将结点都存入数组中,注意存入的时候要用head存,而不是指向他头节点的指针存,

然后用指针数组解引用排序cmp的方法排序,再把他们一一连接起来。

总结:真nm难啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值