实战数据结构(3)_两个单链表间的合并操作

/************************************************************************/
/* 要求:
链表a非递减,链表b非递减
现在要求:
1.实现两个链表的合并,并且元素也是非递减
2.不能占用多余节点,即只能用已有的节点,不能开辟额外的节点
exp:
list a:2 4 4 5 7 8 
list b:3 4 6 9 
则合并后:
list a: 2 3 4 4 4 5 6 7 8 9 
                              */
/************************************************************************/
/************************************************************************/
/* 问题:
  1.还是不能一次写对代码,还是要考编译器来查找错误,指针使用很容易内存泄露
  2.还是在最后一个节点时候,scur=NULL了。就不能引用了scur->data内存泄露

                                                                  */
/************************************************************************/


#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int data;
	struct node *next;
}listnode;


void CreateList(listnode *head,int nodenum,int *data);
void printflist(listnode *head);
void DeleElement(listnode *lista,listnode *listb,int *leave,int dataa);
void mergelist(listnode *head1,listnode *head2,int length_a,int length_b);

void main()
{
	int dataa[]={2,4,4,5,7,8};
	int datab[]={3,4,6,9,11,25,69,78};
	listnode *head1=(listnode *)malloc(sizeof(listnode));
	listnode *head2=(listnode *)malloc(sizeof(listnode));
	if(head1==NULL|| NULL==head2)
	{
		printf("头结点malloc分配失败\n");
		exit(-1);
	}
	 int length_a=sizeof(dataa)/sizeof(dataa[0]);
	 int length_b=sizeof(datab)/sizeof(datab[0]);
	CreateList(head1,length_a,dataa);
	printf("链表A的元素有%d个:\n",length_a);
	printflist(head1);
	CreateList(head2,length_b,datab);
	printf("链表B的元素有%d个:\n",length_b);
	printflist(head2);
	mergelist(head1,head2,length_a,length_b);
}

void CreateList(listnode *head,int nodenum,int *data)
{
	
    listnode *h=head,*pre=h,*newnode;   
    for(int i=0;i<nodenum;i++)  
    {     
        if(NULL==(newnode=(listnode *)malloc(sizeof(listnode))))//开辟一个新节点  
        {  
            printf("malloc申请失败");  
            return ;  
        }  
		newnode->data=data[i];
        pre->next=newnode;  
        pre=newnode;  
    }  
    pre->next=NULL;  
    return ;  

}

void printflist(listnode *head)
{
	listnode *p=head->next;
	while(p!=NULL)
	{	
		printf("%4d",p->data);
		p=p->next;
	}
	printf("\n");
	return ;	
}

void DeleElement(listnode *lista,listnode *listb,int *leave,int dataa)
{
	//链表a删除b在a中出现的元素
	listnode *cur_a=lista->next,*pre_a=lista,*cur_b=listb->next;
	int count=0;
	while(cur_a!=NULL) //控制整个流程
	{
		while(cur_b!=NULL && cur_a->data!=cur_b->data )		//将链表b中的元素在a中遍历
				cur_b=cur_b->next;
		if(NULL!=cur_b)		//找到了 将a中此节点 删除
		{
			pre_a->next=cur_a->next;
			free(cur_a);
			cur_a=pre_a->next; //这里漏掉了    当前元素也要后继一下
			count++;
		}
		else	//当前a指向的元素在b中都没有,cur_a向后移动一位
		{	
			pre_a=cur_a;
			cur_a=cur_a->next;
		}
			cur_b=listb->next; //不管查找了没有 b都需要从新开始匹配
	}
	*leave=dataa-count; //返回剩下a中元素个数
	return ;
}

void mergelist(listnode *head1,listnode *head2,int length_a,int length_b)
{
	printf("合并两个链表后元素个数为%d且不递减顺序为:\n",length_a+length_b);
	listnode *long_head=(length_a>=length_b)? head1:head2;
	listnode *short_head=(length_a<length_b)? head1:head2;//先比较两个链表的长度,短的用来控制主流程 时间复杂度低
	listnode *lpre=long_head,*scur=short_head->next; //lpre指向long的头结点,scur指向short第一个节点
	listnode *lcur=lpre->next; //lcur指向long的第一个节点
	listnode *temp;
	while(lcur!=NULL && scur!=NULL) //short控制主流程 当short NULL时结束 并且long不为最后一个节点
	{
		while(scur->data > lcur->data) //当short>long时,long 后继一位
		{	//当遇到最后一个节点时候,lcur==NULL了,不能再lcur->data了。这里 lcur!=NULL要放到逻辑前
			lpre=lcur;
			lcur=lcur->next;
		}
		if(scur->data <= lcur->data) //当short遇到不大于的数了 此时lcur就是要插入的点
		{	
			temp=scur->next;	//记录short下一个节点
			lpre->next=scur;
			scur->next=lcur;    //将scur插入到long中
			lpre=lpre->next;	//这里lpre后继一位 保持在lcur前一个节点 lcur不变
			scur=temp;		//scur 后继一位
		}
	}
	if(NULL==lcur)	//short 要插入的节点为最后一个节点 直接插入到最后
	{	
		lpre->next=scur; //直接插入
		scur=scur->next;	//到了最后一个节点了,short也应该是最后一个节点了 要scur=NULL
	}
	free(short_head);		//注意要释放短的头节点
	printflist(long_head);
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值