链表的常见操作(转)

链表的常见操作

  链表是数据结构的重要内容,在计算机程序中应用广泛,同时也是各公司笔试题目的重点。

  以下简单实现了链表的一些操作,包括创建、增加节点、删除节点、单链表逆置、合并有序链表等。

一、链表创建


  链表主要有三种形式,包括单链表、双链表和循环链表。

  单链表每个节点只包含一个后驱指针,双链表节点同时包含一个前驱指针和一个后驱指针,循环链表的尾节点的后驱指向头节点。

  代码如下:

/*单链表节点结构*/
typedef
struct NodeType
{
char
elem;
NodeType
*next;
}Node;

/*双链表节点结构*/
typedef
struct
DNodeType
{
char elem;
DNodeType
*next;
DNodeType
*prev;
}DNode;

创建单链表

创建双链表

创建循环链表

二、链表操作

  包括单链表的增加节点、删除节点、输出链表等

添加节点

删除节点

输出链表

三、单链表逆置

  单链表逆置在各公司的笔试题中比较常见,以下是其中一种实现。

  算法描述:将链表中每一个节点插入到头结点之后。

  

  代码如下:

单链表逆置

四、求单链表中间节点

  在笔试题中比较常见,通常题目描述是:给出一个单链表,不知道节点N的值,怎样只遍历一次就可以求出中间节点。

  算法描述:设立两个指针p1,p2,p1每次移动1个节点位置,p2每次移动2个节点位置,当p2移动到尾节点时,p1指向中间节点。

  

  代码如下:

求中间节点

五、合并有序单链表

  问题描述:合并2个有序单链表,合并后的链表也是排好序的。

  算法描述:对链表A中的每一个节点元素,查找其在链表B中的插入位置,并在B中插入该元素。

  代码如下:

合并有序单链表

六、判断链表是否有环


  判断链表是否有环即是判断链表是否为循环链表,算法较为简单,一次遍历判断尾指针是否指向头指针即可。

  代码如下:

判断链表是否有环

七、总结

  以上实现了链表的一些常见操作,源文件LinkList.cpp全部代码如下: 



/*

* 作者: 达闻东
* 修改日期: 2010-04-28 17:10
* 描述: 实现链表的常见操作
*

*/
#include<iostream>
#include<iomanip>
usingnamespace
std;

/*单链表节点结构*/
typedef
struct NodeType
{
char
elem;
NodeType
*next;
}Node;

/*双链表节点结构*/
typedef
struct
DNodeType
{
char elem;
DNodeType
*next;
DNodeType
*prev;
}DNode;

/*=============================================================================*/
/*
创建链表
*/
Node
* CreateList(Node
*head)
{
if(NULL
==
head)//分配头节点空间
head=(Node*)malloc(sizeof(Node)),

head->next=NULL;

Node *current=head ,
*temp;
char
ch;

while(1)

{
cout<<"\n input
elem:";

cin>>ch;
if(‘#‘==
ch)
/*#结束输入*/
break;

temp=(Node *) malloc
( sizeof(Node) );

temp->elem=ch;

temp->next=NULL;

current->next=temp;

/*当前节点的后驱指向新节点*/
current=temp;

/*当前节点为链表尾节点*/

}

return
head;
}
/*=============================================================================*/
/*
输出链表
*/
void
PrintList(Node *head)
{
Node
*
current=head->next;

cout<<"\n List
are:";
while(NULL
!= current)

{
if(NULL !=
current->elem)

cout<<setw(5)<<current->elem;

current=current->next;

}

cout<<"\n";
}

/*=============================================================================*/

/*插入节点*/

Node
*InsertNode(Node *head ,
char elem)
{
if( NULL
== head || NULL
== elem )
return
head;

Node
*current=head->next;
/*当前节点*/
Node
*prev=head;
/*前驱节点*/
Node
*temp;
/*过渡节点*/

while(current)

/*移动至尾节点*/

{
prev=current;

current=current->next;

}

temp=(Node*) malloc(
sizeof(Node) );

temp->elem=elem;

temp->next=NULL;

prev->next=temp;

/*尾节点的后驱指向新节点*/

return
head;

}

/*=============================================================================*/

/*删除节点*/
Node
*DeleteNode(Node
*head,char
elem)
{
if(NULL ==
head || NULL ==
elem)
return
head;
if(NULL ==
head->next)
return
head;

Node
*prev,*current;

prev=head;

current=head->next;

while(current)

{
if(current->elem
== elem)
/*匹配节点元素*/

{
prev->next=current->next;

/*前驱节点的后驱指向当前节点的下一个节点*/
free(current);

/*释放当前节点*/
return
head;
}
prev=current;

current=current->next;
/*移动至下一个节点*/

}

return
head;

}

/*=============================================================================*/

/*单链表逆置*/
Node
*ReverseList(Node
*head)
{
if(NULL
== head)
return
head;
if(NULL ==
head->next)
return
head;
if(NULL ==
head->next->next)
return
head;

Node
*curr=head->next;

/*当前节点*/
head->next=NULL;

Node
*temp;

while(curr)

{

temp=curr->next;
/*暂存下一个节点*/
/*把当前节点插入到head节点后*/
curr->next=head->next;

head->next=curr;

curr=temp;
/*移动至下一个节点*/

}

return
head;

}

/*=============================================================================*/

/*求中间节点*/
Node
* MiddleNode(Node
*head)
{
if(NULL
== head)
return
head;
if(NULL ==
head->next)
return
head->next;

Node
*p1,*p2;

p1=head;

p2=head;

while(p2->next)

{
/*p2节点移动2个节点位置*/
p2=p2->next;
if(p2->next)

/*判断p2后驱节点是否存在,存在则再移动一次*/
p2=p2->next;
/*p1节点移动1个节点位置*/
p1=p1->next;

}
return
p1;

}

/*=============================================================================*/

/*合并有序单链表*/
Node
* MergeList(Node * h1,Node
* h2)
{
if(NULL
== h1 || NULL
== h2)
return
h1;
if(NULL ==
h1->next )
return
h2;
if(NULL ==
h2->next)
return
h1;

Node *
curr1,*curr2,*prev1,*temp;

prev1=h1;
/*链表1的前驱节点*/
curr1=h1->next;

/*链表1的当前节点*/
curr2=h2->next;

/*链表2的当前节点*/
temp=h2;
while(curr2)

{
while(curr1
&& curr1->elem
<
curr2->elem)/*链表1指针移动至大或等于链表2当前元素的位置*/
prev1=curr1,curr1=curr1->next;

/*在链表1中插入链表2的当前元素*/
temp=curr2->next;/*暂存链表2的下一个节点*/
prev1->next=curr2;

curr2->next=curr1;

/*链表1移动至新节点*/
curr1=curr2;
/*链表2移动至下一个节点*/
curr2=temp;

}

return
h1;

}

/*=============================================================================*/

/*创建双链表*/
DNode
* DoubleList(DNode
*head)
{
if(NULL
==
head)//分配头节点空间
head=(DNode*)malloc(sizeof(DNode))
, head->prev=NULL ,
head->next=NULL;

DNode *current=head ,
*temp;
char
ch;

while(1)

{
cout<<"\n input
elem:";

cin>>ch;
if(‘#‘==
ch)
/*#结束输入*/
break;

temp=(DNode *) malloc
( sizeof(DNode) );

temp->elem=ch;

temp->next=NULL;

current->next=temp;

/*当前节点的后驱指向新节点*/
temp->prev=current;

/*新节点的前驱指向当前节点*/
current=temp;

/*当前节点为链表尾节点*/

}

return
head;
}

/*=============================================================================*/
/*输出双链表*/
void
PrintDoubleList(DNode
*head)
{
if(NULL
== head)
return;

DNode * p;

p=head;

cout<<"\n DoubleList
are:";
while(p->next)

{

p=p->next;
if(p->elem)

cout<<setw(5)<<p->elem;

}

cout<<"\n
DoubleList
are:";
while(p->prev)

{
if(p->elem)

cout<<setw(5)<<p->elem;

p=p->prev;

}

}

/*=============================================================================*/
/*创建循环链表*/
Node*
CycleList(Node
*head)
{
if(NULL
==
head)/*分配头节点空间*/
head=(Node*)malloc(sizeof(Node)),head->next=NULL;

Node *current=head ,
*temp;
char
ch;

while(1)

{
cout<<"\n input
elem:";

cin>>ch;
if(‘#‘==
ch)
/*#结束输入*/
break;

temp=(Node *) malloc
( sizeof(Node) );

temp->elem=ch;

temp->next=NULL;

current->next=temp;

/*当前节点的后驱指向新节点*/
current=temp;

/*当前节点为链表尾节点*/

}
current->next=head;

/*尾节点指向头节点*/
return
head;
}
/*=============================================================================*/

/*判断链表是否有环(循环链表)*/
bool
IsCycleList(Node
*head)
{
if(NULL==
head)
returnfalse;
if(NULL
==
head->next)
returnfalse;

Node
*current=head->next;
while(current)

{
if(head ==
current->next)
returntrue;

current=current->next;

}
returnfalse;
}
int
main()
{
Node *
head,*p;
Node *
head2,*head3;
DNode *
dHead;
char ch;
head
= NULL;

head2=NULL;

head3=NULL;

dHead=NULL;

//head=(Node*)
malloc ( sizeof( Node) );
//head->next =
NULL;

//创建单链表
head=CreateList(head);

PrintList(head);

head2=CreateList(head2);

PrintList(head2);

//插入节点

cout<<"\n
input elem to insert:";

cin>>ch;
InsertNode(head,ch);

PrintList(head);

//删除节点

cout<<"\n
input elem to delete:";

cin>>ch;
DeleteNode(head,ch);

PrintList(head);

//单链表逆置

head=ReverseList(head);

cout<<"\n Reversed
!";

PrintList(head);

//求中间节点

p=MiddleNode(head);

cout<<"\n Middle Node
is:";

cout<<p->elem<<endl;

//合并有序单链表

MergeList(head,head2);

cout<<"\n
Merged!";

PrintList(head);

//创建双链表

dHead=DoubleList(dHead);

PrintDoubleList(dHead);

/*创建循环链表并判断是否有环*/
head3=CycleList(head3);

cout<<IsCycleList(head3);
return0;
}


备注:删除节点的时候少考虑了删除节点为head的情况,增加
if(prev->data
==elem)
{
free(head);
return
current;
}
else
{......}即可

链表的常见操作(转)

时间: 2025-01-08 23:33:24

链表的常见操作(转)的相关文章

动态单链表的传统存储方式和10种常见操作-C语言实现

顺序线性表的优点:方便存取(随机的),特点是物理位置和逻辑为主都是连续的(相邻).但是也有不足,比如:前面的插入和删除算法,需要移动大量元素,浪费时间,那么链式线性表 (简称链表) 就能解决这个问题. 一般链表的存储方法 一组物理位置任意的存储单元来存放线性表的数据元素,当然物理位置可以连续,也可以不连续,或者离散的分配到内存中的任意位置上都是可以的.故链表的逻辑顺序和物理顺序不一定一样. 因为,链表的逻辑关系和物理关系没有必然联系,那么表示数据元素之间的逻辑映象就要使用指针,每一个存储数据元素

java数据结构:单链表常见操作代码实现

一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashMap等集合的底层结构都是链表结构.链表以结点作为存储单元,这些存储单元可以是不连续的.每个结点由两部分组成:存储的数值+前序结点和后序结点的指针.即有前序结点的指针又有后序结点的指针的链表称为双向链表,只包含后续指针的链表为单链表,本文总结的均为单链表的操作. 单链表结构: Java中单链表采用No

C#路径/文件/目录/I/O常见操作汇总

文件操作是程序中非常基础和重要的内容,而路径.文件.目录以及I/O都是在进行文件操作时的常见主题,这里想把这些常见的问题作个总结,对于每个问题,尽量提供一些解决方案,即使没有你想要的答案,也希望能提供给你一点有益的思路,如果你有好的建议,恳请能够留言,使这些内容更加完善. 主要内容:一.路径的相关操作, 如判断路径是否合法,路径类型,路径的特定部分,合并路径,系统文件夹路径等内容:二.相关通用文件对话框,这些对话框可以帮助我们操作文件系统中的文件和目录:三.文件.目录.驱动器的操作,如获取它们的

【代码学习】MYSQL数据库的常见操作

============================== MYSQL数据库的常见操作 ============================== 一.mysql的连接与关闭 -h:指定所连接的服务器位置 -u:数据库的用户名 -p:数据库的密码 1 mysql -u 用户名 -p //连接数据库 2 密码 3 exit //关闭数据库 1 mysql_connect($host,$user,$password); //连接数据库 2 mysql_close() //关闭数据库 二.创建数据

杂【第一天】包括eclipse常见操作,程序调试模式

观看传智播客视频笔记,感谢 eclipse的常见操作: 1.当即热版本低于编译器版本是,会出现bad Vresion number in class file的错误: 2.快捷键: alt+/:模板键 ctrl+1:快速修复 ctrl+shift+o:导包 设置代码阿保存的时候自动格式化:windows->首选项->Java->Editor->save Actions 代码移动:alt+上下键 重置视图:window->reset perspective... 3.典型的字节

[java学习笔记]java语言基础概述之数组的定义&amp;常见操作(遍历、排序、查找)&amp;二维数组

1.数组基础 1.什么是数组:           同一类型数据的集合,就是一个容器. 2.数组的好处:           可以自动为数组中的元素从零开始编号,方便操作这些数据. 3.格式:  (一旦创建,必须明确长度)          格式1:              元素类型   [ ]  数组名  =  new  元素类型  [元素个数即数组的长度]:              示例:int[] array = new int[5];          格式2:           

ArcGIS for Android地图控件的5大常见操作

原文地址: ArcGIS for Android地图控件的5大常见操作 - ArcGIS_Mobile的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/arcgis_mobile/article/details/7801467   GIS的开发中,什么时候都少不了地图操作.ArcGIS for Android中,地图组件就是MapView,MapView是基于Android中ViewGroup的一个类(参考),也是ArcGIS Runtime SDK for

java实现二叉树的常见操作

本文转自:红客联盟 解释:程序调用自身的编程技巧叫做递归. 程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量.递归的能力在于用有限的语句来定义对象的无限集合. 递归的三个条件: 边界条件 递归前进段 递归返回段 当边界条件不

【数据结构】两个单循环链表的连接操作

如果在单链表或头指针表示的链表上操作这个比较消耗性能,因为都需要遍历第一个链表,找到an,然后将b1链接到an的后面,时间复杂度是:O(n).若在尾指针表示的单循环链表上实现,则只需改变指针,无需遍历,时间复杂度是:O(1) 现在看算法实现,需要4个主要步骤,都注释了: LinkList Connect(LinkList A,LinkList B){ //假设A,B为非空循环链表的尾指针 LinkList p = A->next;//1.保存A链表的头结点的位置 A->next = B->