您的位置:首页 > 理论基础 > 数据结构算法

Tekson的数据结构程序1——单链表

2009-10-08 17:02 183 查看

1. 单链表

(1)实现一个单链表

【注】这里不是要求创建一个单链表类,否则会涉及到许多的类成员函数的定义,而这只是一个小编程题而已,不要小题大做。这里仅仅是要求建立一个(特定的)单链表而已。

#include <stdafx.h>

#include <iostream>

using namespace std;

struct Node

{

int data;

Node *next;

Node(const int _data=0, Node *_next=NULL):data(_data), next(_next){}

};

int main()

{

int a[10] = {1,2,3,4,5,6,7,8,9,10};//笔试中的程序不必用scanf或cin进行输入,直接定义一个数组说明问题即可!

Node *head = new Node(a[0]);

Node *p = head; //p为遍历链表的指针

for(int i=1; i!=10; ++i)

{

Node *newNode = new Node(a[i]);

p->next = newNode;

p = p->next;

}

//p->next = NULL; //【注】如果不定义默认构造函数的话则千万不能落掉这一条语句!

for(p=head; p!=NULL; p=p->next) //既然是链表,就应该用指针或迭代器来对齐遍历

cout << p->data << " ";

cout << endl;

}

(2)单链表的删除

void deleteNode(Node *&head, const int item) //注意这里是对head的引用,从而可以返回新的链表首地址

{

Node *currPtr, *prevPtr;

for(prevPtr=NULL, currPtr=head; currPtr!=NULL && currPtr->data!=item; prevPtr=currPtr, currPtr=currPtr->next);

if(currPtr == head) //第一种特殊情况:被删除结点为表首结点

{

head = head->next;

delete currPtr;

}

else if(NULL == currPtr) //第二种特殊情况,未找到数据等于item的结点

{

cerr << "can not find " << item << endl;

exit(1);

}

else //一般情况

{

prevPtr->next = currPtr->next;

delete currPtr;

}

}

(3)单链表的插入

void insertNode(Node *&head, const int item)//注意这里是对head的引用,从而可以返回新的链表首地址

{

Node *currPtr, *prevPtr; //prevPtr是currPtr前一个结点的指针

Node *newNode = new Node(item);

for(prevPtr=NULL, currPtr=head; currPtr!=NULL && item>=currPtr->data; prevPtr = currPtr, currPtr=currPtr->next);

//【注】在for循环的终止条件中,currPtr!=NULL必须在currPtr->data<newNode->data前面,否则,当currPtr==NULL时,不会

//再有currPtr->data,于是运行出错。这是由于“&&”前后的语句执行的顺行问题造成的,如果前面的条件不符合,后面的条

//件语句则不再执行。事实上,如果将currPtr->data<newNode->data放在循环内作为if条件的话就不会有这种情况了,不过这

//样会出现冗余的循环。

if(NULL == prevPtr) //第一种特殊情况:新结点的值小于原链表头结点的值,新结点应插入表首(等价于NULL == prevPtr)

{

newNode->next = head;

head = newNode;

}

else if(NULL == currPtr) //第二种特殊情况:节点的值大于原链表最后一个结点的值,新结点应插入表尾

prevPtr->next = newNode;

//【注】这里如果改为currPtr = newNode则为错误的,因为prevPtr->next仍然为NULL,而currPtr = newNode仅仅是修改

//了currPtr这个指针的值而已,而prevPtr->next并未改变(因为二者并未通过指针或引用相联系)。

else//新结点应插入链表中间

{

newNode->next = currPtr;

prevPtr->next = newNode;

}

}

(4)单链表的打印

void printList(const Node *head)

{

const Node *currPtr; //由于currPtr不是const指针,故不用在声明时初始化。

//这里指针类型必须为const Node *型,否则由于const Node *不能转化为Node *而导致语句“currPtr = head”编译出错。

for(currPtr = head; currPtr!=NULL; currPtr=currPtr->next)

cout << currPtr->data << " ";

cout << endl;

}

(5)单链表的测长

int sizeList(const Node *head)

{

int n = 0;

const Node *currPtr; //由于currPtr不是const指针,故不用在声明时初始化。

for(currPtr=head; currPtr!=NULL; currPtr=currPtr->next)

++n;

return n;

}

(6)单链表的排序

选择排序法:

void selectSort(Node *head) //这里参数的类型是不是引用都一样,因为函数体并没有对结点的位置做变化,而改变的仅仅是内容

{

Node *p1, *p2, *smallPtr;

for(p1=head; p1->next!=NULL; p1=p1->next)

{

smallPtr = p1;

for(p2=p1->next; p2!=NULL; p2=p2->next)

if(p2->data < smallPtr->data)

smallPtr = p2;

swap(p1->data, smallPtr->data); //结点本身的位置并不交换,而只交换两个结点之间的数据,从而使问题得到了简化

}

}

交换排序法:

void exchangeSort(Node *head) //这里参数的类型是不是引用都一样,因为函数体并没有对结点的位置做变化,而改变的仅仅是内容

{

if(NULL == head)

return;//防止后面调用直接成员操作符时空结点的未定义错误

Node *p1, *p2;

for(p1=head; p1->next!=NULL; p1=p1->next)

{

for(p2=p1->next; p2!=NULL; p2=p2->next)

{

if(p2->data < p1->data)

swap(p1->data, p2->data);

}

}

}

冒泡排序法:

void bubbleSort(Node *head)//这里参数的类型是不是引用都一样,因为函数体并没有对结点的位置做变化,而改变的仅仅是内容

{

Node *p1, *p2; //用于排序

Node *lastExchangePtr; //冒泡排序法的主线

//遍历链表,使得p1指向链表的最后一个结点,从而为下面冒泡法的循环做准备

for(p1=head; p1->next!=NULL; p1=p1->next);

for(; p1!=head; p1=lastExchangePtr)

{

lastExchangePtr = head; //防止内循环没有执行以致lastExchangePtr没有改变从而导致死循环

for(p2=head; p2!=p1; p2=p2->next)

{

if(p2->data > p2->next->data)

{

swap(p2->data, p2->next->data);

lastExchangePtr = p2;

}

}

}

}

【注】单链表排序不能用插入排序法进行排序,因为插入排序法涉及到了逆序遍历。外层循环为:for(int i=1; i<n; ++i),而内层循环为:for(int j=i-1; j>0 && a[j]>temp; --j),其中“--j”在单链表中没法表示。虽然冒泡排序法是外后内前,在外层也是逆序,但是其实现为for(i=n-1; i>0; i= lastExchangePos),它通过lastExchangePos的减小达到逆序的目的,从而不必通过指针依次逆序遍历指针,因此可以在单链表中实现。

(7)单链表的逆置

方法一(更好):

void reverseList(Node *&head)

{

if(NULL==head || NULL==head->next) //特殊情况:空链表或只有一个结点

return;

Node *prevPtr, *currPtr, *postPtr;//需要三个指针

for(prevPtr=NULL,currPtr=head,postPtr=head->next; currPtr->next!=NULL;

prevPtr=currPtr,currPtr=postPtr,postPtr=postPtr->next)//从第一个结点开始进行遍历,最后一个结点未连接

currPtr->next = prevPtr;

currPtr->next = prevPtr; //不要丢掉,因为最后一个结点没有和之前的结点像连接

head = currPtr;

}

方法二:

void reverseList(Node *&head)

{

if(NULL==head || NULL==head->next)

return;

Node *prevPtr, *currPtr, *postPtr;

prevPtr = head;

currPtr = head->next; //从第二个结点开始进行遍历,第一个结点未连接

while(currPtr != NULL)//以currPtr == NULL为判别终止条件

{

postPtr = currPtr->next; //仅当currPtr非NULL时才会有postPtr

currPtr->next = prevPtr; //每次逆置一次

prevPtr = currPtr; //指针后移,以便进行下一次循环

currPtr = postPtr;

}

head->next = NULL;

head = prevPtr;

}

(8)求单链表的中点

【要求】链表的结点数未知,怎样遍历一次就得出单链表的中间结点?

Node *searchMiddleNode(Node *head)

{

if(NULL == head) //而如果为空链表,NULL->next会导致运行错误,故需单独考虑

return NULL;

if(NULL == head->next) //而如果为单结点链表,NULL->next->next会导致运行错误,故需单独考虑

return head;

Node *p1, *p2;

for(p1=head, p2=head; p1->next!=NULL && p1->next->next!=NULL; p1=p1->next->next, p2=p2->next);

//一定要先检查p1->next!=NULL再检查p1->next->next!=NULL,顺序不能颠倒

return p2;

}

(9)合并两个已排序的链表

【要求】合并两个从小到大已排好序的链表,并形成一个新的具有同样顺序的链表

//【合并思想】首先的确定下新的表头结点;其次,currPtr指向最近加入到新链表中的结点,而它所指向的

//是min(head1->data, head2->data)所对应的结点(假如是head1),进而currPtr指向该结点(最近加入到

//链表中的结点);而head1指向该结点的下一个结点,以便进行下一次比较。知道head1或head2为NULL时,

//而未空的那一个子链表直接连到currPtr后即可。

Node *merge(Node *head1, Node *head2)

{

Node *head=NULL, *p;//新链表的表头head和遍历指针p

Node *p1, *p2, *smallPtr;

//p1和p2分别为待合并链表head1和head2的遍历指针,smallPtr为指向两个结点中值较小的结点的指针

for(p1=head1, p2=head2; p1!=NULL && p2!=NULL;)

{

if(p1->data < p2->data)

{

smallPtr = p1;

p1 = p1->next;

}

else

{

smallPtr = p2;

p2 = p2->next;

}

if(NULL == head)

{

head = smallPtr;

p = head;//p指向最近加入到新链表中的结点,即smallPtr

}

else

{

p->next = smallPtr;

p = p->next;//p指向最近加入到新链表中的结点,即smallPtr

}

}

//根据上面的循环终止条件,不是head1等于NULL就是head2等于NULL

if(NULL == p1)

p->next = p2;

else

p->next = p1;

return head;

}

(10)查找两个单链表中相同的元素

int calNumOfSame(int c[], Node *head1, Node *head2)//返回相同元素的个数

{

Node *p1, *p2;

int k = 0;

//先对两链表进行排序,然后再查找相同元素的个数

selectSort(head1);

selectSort(head2);

for(p1=head1, p2=head2; p1!=NULL && p2!=NULL;)

{

if(p1->data < p2->data)

p1 = p1->next;

else if(p1->data > p2->data)

p2 = p2->next;

else

{

c[k++] = p1->data;

p1 = p1->next;

p2 = p2->next;

}

}

return k;

}

//测试程序:

int main()

{

int a[] = {10,9,5,8,6,7,4,3,2,1};

int b[] = {10,8,6,2,4};

Node *head1, *head2, *p1, *p2;

head1 = new Node(a[0]);

p1 = head1;

head2 = new Node(b[0]);

p2 = head2;

for(int i=1; i!=10; ++i)

{

Node *newNode = new Node(a[i]);

p1->next = newNode;

p1 = p1->next;

}

for(int i=1; i!=5; ++i)

{

Node *newNode = new Node(b[i]);

p2->next = newNode;

p2 = p2->next;

}

int c[10];

int k = calNumOfSame(c, head1, head2);

for(int i=0; i<k; ++i)

cout << c[i] << " ";

cout << endl;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: