在单链表中,存储结构是每一个节点的结构,每个节点是一个单独的结构.其中包含数据域(就是这个data变量,我们用它通指数据)和指针域(一个指向下一个节点的指针).
而在顺序表中,存储结构则是一个包含数组和变量的代表顺序表的结构体,也就是说顺序表只有一个结构,而单链表是许多节点的结构组成的,这是有些不一样的.
typedef struct Node{
ElementType Data;
struct Node *Next; //定义一个指向struct Node结构体的指针
}List;
关于顺序表和链表中查找一个元素
查找某个元素,链表和顺序表好像都一样,都要遍历,从头到尾比较,是不是这个元素.而顺序表好像可以通过算法(二分查找)让查找不用一个一个比较,而是只比较一部分.不知链表有没有这种让查找不用一个个比较的办法.
关于顺序表和链表中插入一个元素
顺序表插入一个元素需要把很多元素往后挪,而链表插入一个元素不需要,如果数据量巨大的话,节省了大量把元素往后挪的时间啊. 数据量小好像体现不出什么差别
关于顺序表和链表中删除一个元素
顺序表删除一个元素需要把很多元素往前挪,而链表删除一个元素只需要改变一下指针指向,如果数据量巨大的话,节省了大量时间啊. 数据量大就会体现差别
关于顺序表和链表在物理存储中存储
顺序表是顺序存储的,挨着盘儿地存,都存在一块儿;
而链表则是,一个结点就是一块儿,不同结点可以存储在不同的位置.
问题一:链表的长度是多少?
在顺序表中,我们需要知道顺序表的长度,只需要顺序表结构体中变量的值,也就是length的值.
而在链表中,显然无法这样做,因为整个链表不是一个结构体,我们要知道链表的长度,只有用循环遍历一遍链表.
求表长:
int length(List *Ptrl)
{ List *p = Ptrl;//设一个临时的指针P指向表的第一个结点
int j =0;
while(p){
p = p->Next;
j++;
return j;
}
问题二(查找): 第k个元素在哪里儿,是什么,返回一个指向这个元素的指针? 元素x是否在这个链表,在,请返回指向它的指针,不在,返回NULL.
留待思考:如何得到 元素x(x指的是值)处于链表中的第几个位置. 如果又想返回位置,又想返回指向x的指针怎么办? 返回值最多只能有一个,百度说:超过一个的有两种解决办法,第一返回指针,第二,返回结构体
利用FindKth()方法查找,得到一个指向第k个元素的指针.
1.按序号查找: (注意是按序号查找,k并不是我们要查找的元素的值,而是我们要查找元素的序号,它在链表的第几个位置)
L转存失败重新上传取消ist *FindKth( int k, List *Ptrl )//返回类型为List类型的指针,参数为K和指向第一个结点的指针
{ L转存失败重新上传取消ist *p = Ptrl;
int i = 1; //因为临时变量指向了第一个结点,所以i为1,代表第一个位置
while( p !=NULL && i<k ){
p = p->next;
i++;
}
if( i == k )
return p;
else
return NULL;
}
2.按 元素的值查找:Find()方法
返回的是指向这个元素的指针(临时指针变量)
L转存失败重新上传取消ist *Find(ElementType X,List *Ptrl){//需要传元素X的值X ,它所在链表的头结点指针
List *p = Ptrl;
while( p != NULL && p->Data != X)
p = p->next;
return p;
}
疑问:如果 p->Data = X退出循环,不等于接着找不就行了吗?为什么要p !== NULL这个条件呢?
如果链表中没有这个元素,仅仅根据"p->Data != X"这个条件判断,可能会陷入死循环,因一直找不到,所以会一直找,导致退不出循环.p != NULL是让循环保证在链表内判断有没有元素X,如果出了链表范围,再找到了也是无意义的.出了链表,就会返回一个空指针.说明查找不成功.
只有指针p不等于空才有判断它的Data是不是X的意义,如果p=null了,说明已经出了链表,判断p->Data != X已无意义,直接退出循环.
只有p != NULL(说明在链表范围内)且p->Data != X(还没找到X)才有进入循环的意义.
仅仅"p->Data != X"一个条件无法让循环正常进行不陷入死循环.加入p != NULL 是让循环在链表所属范围内进行,出了链表这个范围就得退出循环,不然不是链表范围了你还在那儿瞎找?
问题三:为什么要有一个临时的指针变量指向第一个结点?不是已经有一个指针变量指向第一个结点了吗?
因为已有的那个指针变量指向了第一个结点,我们做查询操作,它的内容是不允许改变的,如果改变了,就破环了这个链表.
所以需要一个临时变量来辅助我们查询,为了不改变和破环这个我们要查询的链表.
看看这一步,p = p->Next; 如果不用辅助的临时变量进行操作,这就会改变指针指向的某一块的内容,从而破环整个链表,而利用这个临时变量,就可以不破环链表来操作得到我们想要的结果.
链表的插入:
在第i-1个结点后插入一个元素
一定要知道我们待插入的元素的前面一个是谁,才能把它插进去.可能因为需要让它前面一个元素的Next指向它.
(1)先构造一个新结点,用s指向
(2)再找到链表的第i-1个结点,用p指向
(3)然后修改指针,第i-1个结点的next要指向新结点,新结点的next要指向之前第i-1个结点的下一个结点,这样就完成了插入新结点的操作
利用两条语句,s->Next = p->Next ; p->Next = s ; 这两条语句的顺序不能变.
如图所示:
插入操作实现:
List *Insert(ElementType X,int i,List *PtrL) //在指针PtrL指向的链表中的第i个位置插入元素X { List *p,*s; if( i == 1){ s = (List*)malloc(sizeof(List));//为新结点申请内存创建一个新结点,将新结点插在表头 s->data = X; s->Next = PtrL; return s; } p = FindKth( i-1,Ptrl); if ( p == NULL ){ print("参数错误"); return NULL; }else { s = (List *)malloc(sizeof(List)); s->Data = X; s->Next = p->Next; P->Next = s; return PtrL; } }
链表结点的删除(删除链表的第i个位置上的结点):
(1)先找到链表的第i-1个结点,用p指向;
(2)再用指针s指向要被删除的结点(p的下一个结点);
(3)然后修改指针,p->Next = s->Next;再删除s所指的结点;
(4)最后释放s所指结点的空间;(C语言就是这样,你申请了空间,不用了要释放,Java这时候估计不用自己释放)
删除操作实现:
List *Delete( int i,List *PtrL) { List *p,*s; if( i == 1 ){ //若要删除的是表的第一个结点 s = PtrL; //s指向第一个结点 if(Ptr !=NULL) //从链表删除 PtrL = PtrL->Next; else return NULL; free(s); return PtrL; } p = FindKth(i-1,PtrL); //查找第i-1个结点 if( p == NULL){ printf("第%d个结点不存在",i-1); return NULL; }else if ( p->Next == NULL ){ printf("第%d个结点不存在",i); return NULL; }else { s = p->Next; //s指向第i个结点 p->Next = s->Next; //从链表中删除 free(s); //释放被删除结点 return PtrL; } }
