2.1线性表及其实现
什么是线性表 ?![](https://i-blog.csdnimg.cn/blog_migrate/5633ca49c23623e7c1f4511b0aa99d16.png)
首先判断插入是否合法。
最后别忘记last -- .
i < 1:空表。
typedef int Position;
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
Position Last;
};
/* 初始化 */
List MakeEmpty()
{
List L;
L = (List)malloc(sizeof(struct LNode));
L->Last = -1;
return L;
}
/* 查找 */
#define ERROR -1
Position Find( List L, ElementType X )
{
Position i = 0;
while( i <= L->Last && L->Data[i]!= X )
i++;
if ( i > L->Last ) return ERROR; /* 如果没找到,返回错误信息 */
else return i; /* 找到后返回的是存储位置 */
}
/* 插入 */
/*注意:在插入位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是存储下标位置(从0开始),两者差1*/
bool Insert( List L, ElementType X, Position P )
{ /* 在L的指定位置P前插入一个新元素X */
Position i;
if ( L->Last == MAXSIZE-1) {
/* 表空间已满,不能插入 */
printf("表满");
return false;
}
if ( P<0 || P>L->Last+1 ) { /* 检查插入位置的合法性 */
printf("位置不合法");
return false;
}
for( i=L->Last; i>=P; i-- )
L->Data[i+1] = L->Data[i]; /* 将位置P及以后的元素顺序向后移动 */
L->Data[P] = X; /* 新元素插入 */
L->Last++; /* Last仍指向最后元素 */
return true;
}
/* 删除 */
/*注意:在删除位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是存储下标位置(从0开始),两者差1*/
bool Delete( List L, Position P )
{ /* 从L中删除指定位置P的元素 */
Position i;
if( P<0 || P>L->Last ) { /* 检查空表及删除位置的合法性 */
printf("位置%d不存在元素", P );
return false;
}
for( i=P+1; i<=L->Last; i++ )
L->Data[i-1] = L->Data[i]; /* 将位置P+1及以后的元素顺序向前移动 */
L->Last--; /* Last仍指向最后元素 */
return true;
}
注意:
以上的定义有点浪费空间。 但以空间换时间,a【N】放N个原始数据,有额外的数据再用链表连接,访问原始数据的时候再用数组访问,更加方便。
讨论2.1 链式存储中FindKth函数的另一种实现?
如果将链式存储中FindKth的函数实现(如下)做个修改:把函数最后的if语句判断条件改为判断p是否为NULL,即:
if (p==NULL) return NULL;
else return p;
或者说直接简化为:return p;
对于这样的修改,程序还正确吗?为什么?
List FindKth( int K, List PtrL ) {
List p = PtrL;
int i = 1;
while (p !=NULL && i < K ){
p = p->Next;
i++;
}
if(i == K) return p; /*找到第K个,返回指针*/
else return NULL; /* 否则返回空 */ }
答:如果保证输入的K能大于0,可以这样修改,但是如果K<=0时,应该返回NULL,修改后就返回头结点指针,显然不对
空表插入和非控表插入?在链表中设置头节点有什么好处?
便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头节点的非空指针,因此空表和飞控表的处理也就统一了。
便于首元节点的处理
首元结点的地址位置保存在头节点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无需进行特殊处理;
千万别忘记free掉
下面的了解即可:
typedef struct LNode *PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
/* 查找 */
#define ERROR NULL
Position Find( List L, ElementType X )
{
Position p = L; /* p指向L的第1个结点 */
while ( p && p->Data!=X )
p = p->Next;
/* 下列语句可以用 return p; 替换 */
if ( p )
return p;
else
return ERROR;
}
/* 带头结点的插入 */
/*注意:在插入位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是链表结点指针,在P之前插入新结点 */
bool Insert( List L, ElementType X, Position P )
{ /* 这里默认L有头结点 */
Position tmp, pre;
/* 查找P的前一个结点 */
for ( pre=L; pre&&pre->Next!=P; pre=pre->Next ) ;
if ( pre==NULL ) { /* P所指的结点不在L中 */
printf("插入位置参数错误\n");
return false;
}
else { /* 找到了P的前一个结点pre */
/* 在P前插入新结点 */
tmp = (Position)malloc(sizeof(struct LNode)); /* 申请、填装结点 */
tmp->Data = X;
tmp->Next = P;
pre->Next = tmp;
return true;
}
}
/* 带头结点的删除 */
/*注意:在删除位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是拟删除结点指针 */
bool Delete( List L, Position P )
{ /* 这里默认L有头结点 */
Position pre;
/* 查找P的前一个结点 */
for ( pre=L; pre&&pre->Next!=P; pre=pre->Next ) ;
if ( pre==NULL || P==NULL) { /* P所指的结点不在L中 */
printf("删除位置参数错误\n");
return false;
}
else { /* 找到了P的前一个结点pre */
/* 将P位置的结点删除 */
pre->Next = P->Next;
free(P);
return true;
}
}
拓展:
第二种好,因为不仅方便找到头结点,还能方便找到尾结点。
先后顺序无所谓
![]()
注意:不要让在插入s之前就将其前驱断掉,否则找不到。
![]()
next可以当作指针来用
静态链表:删除操作后原有空间无法被释放,会造成空间的浪费。
顺序表:1 链表:2