算法导论6.5-8

题目:k个已排序链表,元素总数是n,要求在nlgk时间复杂度内把k个链表合并为一个有序链表。

思路:取k个链表中每一个链表的第一个元素,共k个,将这k个元素建立一个最大堆(或最小堆),取出堆中的第一个元素放到数组str中,将堆中第一个元素所对应的链表中的该元素去掉,链表中的第二个元素上升为第一个元素。并把该元素插入到最大堆中的根节点,调整最大堆,然后仍然取出堆中根元素放入到str中,将该元素对应的链表下一个元素放入堆中,调整最大堆。当某一个链表中的元素取光时,将堆中的最后一个元素放到根节点,调整堆。直到所有链表中元素取光为止。

编写代码时,我遇到的一个小问题是,如何根据根元素的值确定该根元素属于哪一个链表,如果进行匹配搜索则时间复杂度就会变成nkLg(k).此处建立一个二维数组temp[100][2],其中temp[i][0]放链表的元素大小,temp[i][1]该元素属于第几个链表。以空间换时间的思路解决这个小问题。本代码写的有点麻烦,存放于链表中的元素完全可以直接放到数组中,但是为了倾向于面向对象的设计,本代码采用了建立链表对象的方法。具体代码如下:

LinkNode.h

#include<iostream>
using namespace std;
class Link;
class LinkNode
{
private:
	int linkID;
	int key;
	LinkNode * next;
public:
	friend Link;
	LinkNode():next(NULL)
	{}
	LinkNode(int ID,int num,LinkNode *node=NULL):linkID(ID),key(num),next(node)
	{
		
	}
	~LinkNode()
	{
		next=NULL;
	}
	int Getkey()
	{
		return this->key;
	}
	int GetLinkID()
	{
		return linkID;
	}
};


Linki.h

//链表,实现了链表元素的排列,链表元素个数计算,元素删除
//元素添加链表元素查找。
#include<iostream>
#include"LinkNode.h"
using namespace std;
class Link
{
private:
	LinkNode* head;
	int length;
public:
	Link():head(new LinkNode())
	{
		length=1;
	}
	~Link()
	{
		MakeEmpty();
		delete head;
	}
	void MakeEmpty()
	{
		if(head->next==NULL)
		{
			return ;
		}
		LinkNode * p=head;
		while(head->next!=NULL)
		{
			p=head->next;
			head->next=p->next;
			delete p;
		}
	}
	//使用数组初始化链表
	void Init(int num,int a[],int len)
	{
		int i;
		this->length=len;
		LinkNode * p=head;
		for(i=0;i<len;i++)
		{
			p->key=a[i];
			p->linkID=num;
			LinkNode *q=new LinkNode();
			p->next=q;
			p=p->next;
		}
	}
	int GetLength()
	{
		return length;
	}
	bool DeleteHead()
	{
		if(head==NULL)
		{
			cout<<"the link is empty!"<<endl;
			return 0;
		}
		else
		{
			LinkNode* p;
			p=head;
			head=head->next;
			delete p;
			length-=1;
			return 1;
		}
	}
	LinkNode* GetHead()
	{
		if(head==NULL)
		{
			cout<<"the link is empty!"<<endl;
			return NULL;
		}
		return this->head;
	}
};


算法实现:

#include<iostream>
#include"Link.h"
using namespace std;
#define Left(i) i*2+1
#define Right(i) i*2+2
#define Parent(i) (i-1)/2
//此处添加利用循环方式代替递归Max_Heapify的函数,该函数可以替代Max_Heapify
//在某些情况下能取得更好 的效果
void Max_Heapify1(int (*a)[2],int length,int i)
{
	int left,right,num=i,largest=i;
	left=Left(i);
	right=Right(i);
	while(1)
	{
		if(a[left][0]>a[num][0]&&left<length)
		{
			largest=left;
		}
		//此处逻辑判断出错,开始为else if,发现不能正确排序,改为else之后正确
		if(a[right][0]>a[largest][0]&&right<length)
		{
			largest=right;
		}
		if(num!=largest)
		{
			int temp;
			temp=a[num][0];
			a[num][0]=a[largest][0];
			a[largest][0]=temp;
			temp=a[num][1];
			a[num][1]=a[largest][1];
			a[largest][1]=temp;
			num=largest;
			left=Left(num);
			right=Right(num);
		}
		else 
		{
			break;
		}
	}
	
}
void Build_Max_Heap(int (*a)[2],int length )
{
	int i;
	for(i=(length-1)/2;i>=0;i--)
	{
		Max_Heapify1(a,length,i);
	}
}
void InitLink(int (*b)[5],int k,int m,Link Node[])
{
	int i;
	for(i=0;i<k;i++)
	{
		Node[i].Init(i,b[i],m);
	}
}
void SortLink(Link Node[],int k,int m,int str[])
{
//	int *str=new int[100];
	int temp[100][2]={0};
	int i,j,t=0,count,s=k;
	for(i=0;i<k;i++)
	{
		temp[i][0]=Node[i].GetHead()->Getkey();
		temp[i][1]=Node[i].GetHead()->GetLinkID();
	}
	for(j=1;j<=k*m;j++)
	{
		Build_Max_Heap(temp,s);
		str[t++]=temp[0][0];
		count=temp[0][1];
		Node[count].DeleteHead();
		if(Node[count].GetLength()==0)
		{
			temp[0][1]=temp[s-1][1];
			temp[0][0]=temp[s-1][0];
			s--;
		}
		else
		{
			temp[0][1]=Node[count].GetHead()->GetLinkID();
			temp[0][0]=Node[count].GetHead()->Getkey();
		}
	}
//	return str;
}
int main()
{
	int n,k,m,i;
	int a[4][5]={{9,8,7,6,5},{5,4,3,2,1},{15,14,13,12,11},{29,28,27,26,25}};
	n=20;k=4;m=5;
	Link* Node=new Link[4];
	InitLink(a,k,m,Node);
	int st[100];
	SortLink(Node,k,m,st);
	for(i=0;i<20;i++)
	{
		cout<<st[i]<<" ";
	}
//	delete []st;
	delete []Node;
	cout<<endl;
	cout<<"hHA"<<endl;
	return 0;
}


代码中中需要注意的问题是:

二维数组作为参数传递的问题:

void InitLink(int (*b)[5],int k,int m,Link Node[]),形式如代码中,int(*b)[5],注意中括号中的数字必须写明,不能是int(*b)[]的格式。

第二个问题是:

在一个函数中new的指针,放到main函数中去delete,就会导致进程无法正常终止,按照C++语法应该是没问题,可能是编译器或机器有关。具体研究见后文。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值