/************************************************************************/
/* 要求:
連結串列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);
}