leetcode链表(简单难度)

本文介绍了LeetCode中涉及链表的多个经典问题,包括合并有序链表、删除排序链表重复元素、环形链表判断及相交链表查找等。通过不同解题策略,如暴力法、长度差法等,深入理解链表操作。
摘要由CSDN通过智能技术生成

21.合并两个有序链表

//p->val,other->val,p->next->val前都要确定是否为空
//思路是:先做链表头比较
//小的作为p 是结果链,大的作为other 用于加入p 
//要考虑的特殊情况是单节点链表[1] NULL,以及某个链表先结束
//其他方法有递归(交叉链)或用一条新链

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
   int flag=0;
   struct ListNode *p,*tmp,*q;
   if(l1==NULL)
       return l2;
   if(l2==NULL)
       return l1;
   
   if(l1->val<=l2->val)
   {p=l1;  q=l2; flag=1;}
   else
   {p=l2;  q=l1;}  

   while(p&&q)
   {
       if(p->next ==NULL)
           break;
       if(p->val<=q->val && p->next->val>=q->val  )
       {
            tmp=(struct ListNode*)malloc(sizeof(struct ListNode));
            tmp->next=p->next;
            tmp->val=q->val;
            p->next=tmp;
            q=q->next;
       }            
       p=p->next;
   }

   if(q)
       p->next=q;
   if(flag)
       return l1;
   else
       return l2;
}

83.删除排序链表中的重复元素

//思路是:做一个临时表头,tmphead遍历,每次取tmphead->val做移除同值链表元素
//看错题了,我是删除链表中的重复元素
//如果是排序链表,则只比较当前节点和下一节点,删除或移动到下一节点
struct ListNode* deleteDuplicates(struct ListNode* head){
    struct ListNode *p=(struct ListNode*)malloc(sizeof(struct ListNode)),
                    *tmp=(struct ListNode*)malloc(sizeof(struct ListNode)),
                    *tmphead=(struct ListNode*)malloc(sizeof(struct ListNode));
    int val=0;
    p->next=NULL;
    tmp->next=NULL;
    tmphead->next=NULL;
    tmphead=head;

    while(tmphead)
    {
        p=tmphead;
        tmp=p->next;
        val=p->val;
        while(tmp)
        {
            if(tmp->val==val)
            {
                p->next=tmp->next;
                tmp=p->next;
                continue;
            }
            p=p->next;
            tmp=p->next;
        }
        tmphead=tmphead->next;
    }
    return head;

}

141.环形链表

//官方解释双指针:如果列表中不存在环,快指针将先打到表尾;有环则快指针一定能追上。
//我试了环中节点数为奇偶的情况,笑哭
bool hasCycle(struct ListNode *head) {
    if(head==NULL){
        return 0;
    }
    struct ListNode *p,*q;
	p=head;q=head;
	while(p->next!=NULL&&q->next!=NULL){
		if(p->next==q->next->next){
			return 1;
		}
		p=p->next;q=q->next->next;
        if(p==NULL||q==NULL){
            return 0;
        }
	}
	return 0;
}

160.相交链表

暴力法

//思路:对headA链表的每个节点,遍历headB中每个节点,返回相等节点。
//注意判断条件既不能是val,也不能是next
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *p=(struct ListNode*)malloc(sizeof(struct ListNode)),
                    *q=(struct ListNode*)malloc(sizeof(struct ListNode));
    p=headA;
    q=headB;
    while(p)
    {
        while(q&&p!=q)
            q=q->next;
        if(q)
            break;
        p=p->next;
        q=headB;
    }
    if(p)
        return p;
    else
        return NULL;
}

长度差法

//二.长度差法:由于交会节点后的部分必然相同,所以先计算长度差,长链表先跑,再一起跑并等值比较
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *p = headA, *q = headB;
    int la, lb, lgap;
    for (la = 0; p; p = p->next, la++);  
    for (lb = 0; q; q = q->next, lb++);  
    lgap = la - lb;  
    p=headA;
    q=headB;
    if (la > lb){
        while (lgap--)  p = p->next;  
    } else {
        while (lgap++)  q = q->next;  
    }
    while (p){  
        if (p == q) return p;
        p = p->next;
        q = q->next;
    }
    return NULL;
}

通过“增加”消除长度差

//假设两条链表 A |------***|     B |---***|
//有两种拼接方式,编程中使用重定向拼接:
// A/B   |------***||---***| 
// B/A   |---***||------***|
//显然,同时出发,只有在最末尾才会同时到达相同的部分,
//因为a和b各自第一次到达一定不等时
//观察第一种拼接方式,
//到达B时,后半部分正好是“减法”消除长度差的图示
                  
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
    if(headA==NULL||headB==NULL)return NULL;
    struct ListNode* pA=headA;
    struct ListNode* pB=headB;
    while(pA!=pB)
    {
        pA=pA==NULL?headB:pA->next;
        pB=pB==NULL?headA:pB->next;
        
    }
    return pA;
}

203.移除链表元素

//一个节点==NULL,它不能取val
//思路:出现删除节点时,要有两个遍历节点p和tmp,
//且要按tmp遍历,从而在p和tmp->next形成新链
//也可以将不相等的节点用尾插法建立新链表
struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode *p=(struct ListNode*)malloc(sizeof(struct ListNode)),
                    *tmp=(struct ListNode*)malloc(sizeof(struct ListNode));
    p->next=NULL;
    tmp->next=NULL;
   
    while(head&&head->val==val)
        head=head->next;
     if(head==NULL)
        return head;
    
    p=head;
    tmp=p->next;
    while(tmp)
    {
        if(tmp->val==val)
            {p->next=tmp->next;
            tmp=p->next;
            continue;}
        if(p->next==NULL)    
            break;
        p=p->next;
        tmp=p->next;
    }
    return head;
}

206.反转链表

//处理思维比较复杂的时候,先要明确每个变量的职能,别太急
//思路:tmp用来保存原链,p是原链头,rehead是新链头 
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode *p,*tmp,*rehead;
    p=(struct ListNode *)malloc(sizeof(struct ListNode));
    tmp=(struct ListNode *)malloc(sizeof(struct ListNode));
    rehead=(struct ListNode *)malloc(sizeof(struct ListNode));
    p->next=NULL;
    tmp->next=NULL;
    rehead=NULL;
    p=head;
    while(p)
    {
        tmp=p->next;   //保存原始链结构
        p->next=rehead; //将后节点next指向前节点
        rehead=p;       //新链换头为后节点
        p=tmp;          //p重新指向原始链头
    }
    return rehead;
}

234.回文链表

//我的思路是先反转链表,感觉逻辑没问题,实际上原链被破坏了 
//修改思路:计算链表长,反转一半,奇偶判断消除原始奇链表首部
//运行时间很长,竟然就因为最后没用null,而是用n判断i。用null时间缩短12ms! 
bool isPalindrome(struct ListNode* head){
    struct ListNode *p,*tmp,*rehead;
    p=(struct ListNode *)malloc(sizeof(struct ListNode));
    tmp=(struct ListNode *)malloc(sizeof(struct ListNode));
    rehead=(struct ListNode *)malloc(sizeof(struct ListNode));
    int i=0,n=0;
    rehead=NULL;
    p->next=NULL;
    tmp->next=NULL;
    p=head;
    while(p)
    {
        p=p->next;
        i++;
    }

    p=head;
    while(p&&n<i/2)
    {
        tmp=p->next;
        p->next=rehead;
        rehead=p;
        p=tmp;
        n++;
    }

    if(i%2)
        head=p->next;
    else
        head=p;
    
    
    while(head&&rehead)
    {
        if(head->val!=rehead->val)
            break;
        head=head->next;
        rehead=rehead->next;
        
    }
    if(head)
        return false;
    else
        return true;
}

237.删除链表中的节点

//吐血,直接改值并跳过下一节点 
void deleteNode(struct ListNode* node) {
    node->val = node->next->val;
    node->next=node->next->next;
} 

876.链表的中间节点

//c语言中对于除法运算符,当被除数和除数都是整数时,并不会得到一个double的浮点型的数,而是直接舍去小数部分(即向下取整)
//4个节点,i=4,n从1开始到3;5个节点,i=5,n从1到3
//其他思路:双指针p、q,q每次跳两节点p=p->next->next,p跑完则q跑一半
struct ListNode {
	int val;
	struct ListNode *next;
};
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode *p;
	int i=0,n=1;
	p=head;
	while(p)
    {
		p=p->next;
		i++;
	}
    while(n<i/2+1)
    {
        head=head->next;
        n++;
    }
    return head;
}

1290.二进制链表转整数

//c语言没有次方运算,^不是该符号,可调用<math.h>的pow()
#include<math.h>
int getDecimalValue(struct ListNode* head){
    int i=0,num=0;
    struct ListNode *p;
    p=(struct ListNode *)malloc(sizeof(struct ListNode));
    p=head;
    while(p)
    {
        p=p->next;
        i++;
    }
    p=head;
    while(p)
    {
        num += p->val*pow(2,(i-1));
        p=p->next;
        i--;
    }
    return num;
} 

get flag

1.什么时候需要malloc?
(1)char * p = (char *)malloc(sizeof(char));     
	p = "xxxxxx"; 
第一句临时申请1个char空间,p是这个空间的地址,
但第二句就被赋值为字符串的地址。
则malloc得到的地址丢失了,无法free,导致内存泄漏。
内存泄漏:已动态分配的堆内存 未释放 或 无法释放

(2)临时需要一块内存,这块内存用来存储n个int的变量。
就需要使用malloc为pMax分配一块内存。可以这样做:
pMax = malloc(sizeof(int) * n);
if (pMax == NULL) // 错误处理       { TODO...}
memset(pMax, 0 , sizeof(int) * n);
这样我们就为pMax分配了一块内存大小为sizeof(int) *n 字节的内存。
这里malloc返回一个指向这块内存的首地址并将它赋给了int型指针变量pMax.
初始化后,就可以像数组一样操作这块int型的内存了 
pMax[0]~pMax[n]都可指向整型变量


2.野指针
char *p="abc";        char *q;         strcpy(q,p);
q是野指针,它的值是随机的内存地址,如果该地址的值接下来被直接覆盖,就可能有严重后果。

避免野指针的方法:
char *q=NULL;               
	意思是不指向任何内存地址  
q=(char *)malloc(sizeof(char));    
	malloc给指针分配的内存是没有被其他程序使用的
综上,假如对于一个链表节点p,没有对p进行malloc,
直接p->next=NULL会在leetcode报错(devc会运行很长时间),
这里p->next就可能是随机内存中的值。


3.单链表建表
头插法建表:
新结点s插入到当前链表的表头结点L之后,就是在一个间隙中不断插节点
s->next=L->next; L->next=s;

尾插法建表:
增加一个尾指针r,将新节点插到当前单链表的表尾上
r->next=s; r=s;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值