在单链表的实现上做一些修改就得到双向链表的实现:
具体实现如下:
//双链表实现
#include <stdio.h>
#include <stdlib.h>
#define ElementType int
/*
1 2 3 4 5 6 -1 程序测试数据
*/
typedef struct node{
ElementType data;
struct node *piror;
struct node *next;
}L;
typedef L* List;
List Make_List_head( ); //头插法创建表
List Make_List_tail( ); //尾插法创建表
List find( ElementType x, List ptrl ); //查找元素,返回下标,若没找到返回-1
void insert( ElementType x, int i, List ptrl ); //插入函数,在第i个位置插入新元素
void Delete( int i, List ptrl ); //删除线性表中第i个元素
int length ( List ptrl ); //返回表长(即线性表中数据元素的个数)
List find_k( int i, List ptrl ); //返回线性表中第i个元素
int Empty( List ptrl ); //判断表空,如果表空,返回0;表不空,返回1
void PrintList( List ptrl, int m, int n ); //打印从位置m到位置n的所有元素
void DestoryList( List ptrl ); //销毁链表
int main ( void )
{
List ptrl;
ptrl = Make_List_tail( );
if( !Empty( ptrl ) )
printf( "表空\n" );
int len = length( ptrl );
printf( "表长len = %d\n", len );
PrintList( ptrl, 1, len );
int x = 731;
insert( x, 5, ptrl );
PrintList( ptrl, 1, len + 1 );
Delete( 4, ptrl );
PrintList( ptrl, 1, len );
return 0;
}
List Make_List_head( )
{
List ptrl, s;
ptrl = ( List )malloc( sizeof( L ) );
if( ptrl == NULL ){
printf( "申请空间出错\n" );
exit(0);
}
ptrl -> next = NULL;
ptrl -> piror = NULL;
ElementType x;
scanf( "%d", &x );
while( x != -1 ){ //输入以-1为结束标志
s = ( List )malloc( sizeof( L ) );
if( s == NULL ){
printf( "申请空间出错\n" );
exit(0);
}
s -> next = ptrl -> next;
ptrl -> next = s;
s -> data = x;
ptrl -> next -> piror = s;
s -> piror = ptrl;
scanf( "%d", &x );
}
return ptrl;
}
List Make_List_tail( )
{
List ptrl, s, p;
ptrl = ( List )malloc( sizeof( L ) );
if( ptrl == NULL ){
printf( "申请空间出错\n" );
exit(0);
}
ptrl -> next = NULL;
ptrl -> piror = NULL;
ElementType x;
scanf( "%d", &x );
p = ptrl;
while( x != -1 ){
s = ( List )malloc( sizeof( L ) );
if( s == NULL ){
printf( "申请空间出错\n" );
exit(0);
}
s -> data = x;
p -> next = s;
s -> piror = p;
p = s;
scanf( "%d", &x );
}
p -> next = NULL;
return ptrl;
}
List find_k( int i, List ptrl )
{
int k = 1;
List p = ptrl -> next;
if( i == 0 )
return ptrl;
if( i < 1 )
return NULL;
while( k < i && p != NULL ){
k++;
p = p -> next;
}
return p;
}
List find( ElementType x, List ptrl )
{
List p = ptrl -> next;
while( p -> data != x && p -> next != NULL ){
p = p -> next;
}
return p;
}
void insert( ElementType x, int i, List ptrl )
{
List p = find_k( i - 1, ptrl );
if( p == NULL ){
printf( "查找位置错误\n" );
exit(0);
}
List s;
s = ( List )malloc( sizeof( L ) );
if( s == NULL ){
printf( "申请空间出错\n" );
exit(0);
}
s -> data = x;
s -> next = p -> next;
p -> next -> piror = s;
p -> next = s;
s -> piror = p;
}
void Delete( int i, List ptrl )
{
List p = find_k( i - 1, ptrl );
if( p == NULL ){
printf( "查找位置错误\n" );
exit(0);
}
List s = p -> next;
p -> next = s -> next;
s -> next -> piror = p;
free( s );
}
int length ( List ptrl )
{
int len = 0;
while( ptrl -> next != NULL ){
len++;
ptrl = ptrl -> next;
}
return len;
}
int Empty( List ptrl )
{
if( ptrl -> next == NULL )
return 0;
else
return 1;
}
void PrintList( List ptrl, int m, int n )
{
List p, q;
p = find_k( m, ptrl );
q = find_k( m, ptrl );
int i, len = length( ptrl );
for( i = m; i <= n; i++ ){
printf( "%d ", p -> data );
p = p -> next;
}
printf( "\n" );
}
void DestoryList( List ptrl )
{
List s, p;
s = ptrl -> next;
ptrl -> next = NULL;
while( s != NULL ){
p = s -> next;
free( s );
s = p;
}
}
只是这部分有一个地方不太明白,虽然双向链表可以比较方便的得到某一个数据元素的前驱元,但是在插入和删除操作中至少需要去查找那个我们想要插入的位置,这样一来插入和删除的时间复杂度仍然是O(n),但书上给出的解释是O(1),还不是很明白,留作以后看懂了来解决。
PS:第一次补充:现在我有想到一个解释:就只讨论插入这个算法,而不考虑实际实现它的过程的话,在单链表中,由于定位一个数据元素以后无法找到它的前驱节点,所以它的算法就分两步:
(1)从表头开始找到位置i的前驱节点;(2)更改指针。
所以时间复杂度为O(n)。
在双向链表中,在定位一个数据元素之后,算法只有一步:
(1)修改指针。
所以时间复杂度为O(1)。
但在实际insert函数中它们的时间复杂度都是O(n)。