数据结构(C)必会知识点+易错点+代码:线性表(顺序表建立,删除,定位,合并;链表的正序,逆序建立,删除插入,逆置)
一.顺序表和链表的区别
1.定义
顺序表:地址连续,逻辑相邻则物理相邻,随机存取;
链表:地址不连续,逻辑相邻物理不一定相邻,顺序存取;
二,优缺点
顺序表:易存取,找特定位置上的元素时间复杂度为O(1),难插入和删除(牵涉到移位);
链表:难存取,找特定位置上的元素的时间复杂度为O(n),易插入和删除。
共同点:在找特定关键字,两个表的合并,等需要遍历的操作时,二者时间复杂度相等 。
三,注意
1.防止内存泄露
在建立动态数组时,如果用户输入的数据超过了顺序表的大小,那么多出的数据就会覆盖后面连续的内存单元,可能会导致程序出错。所以在顺序表的定义中有表长,和表的大小这两个数据项,便于控制表长小于等于表的大小。
2.释放内存
所有分配在堆区(程序员分配)的内存都需要进行回收
顺序表:动态数组,free(基地址指针),如果是静态数组,则只需把表长改为0,不需要free;
链表;逐个free该节点的指针,(注意保存被free的当前指针的下一个节点),指针被free了,指针保存的地址没变只是不关联了之前的那块内存,被称为野指针,可以将其指向NULL,防止后续误调用。
(循环链表的释放)
3.插入和删除方面
顺序表:删除从被删处往后,后一个覆盖前一个;
插入从最后到要插入处,每个往后退一个
链表:删除:先保存,再删除,后释放
插入:先连上,后断开,再接上
4.遍历方面(别搞混)
顺序表:i++
链表:p=p->next
四,代码(配套书本严蔚敏版)
1.有序顺序表的合并(包含建立,插入,定位等操作)
#include<stdio.h>
#include<stdlib.h>
#define LIST_INIT_SIZE 100//防止内存泄露
#define LISTINCREMENT 10 //*&
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef int ElemType;
typedef struct{
ElemType *elem;
int length;
int listsize;
}SqList;
//顺序表的建立
Status InitList_Sq(SqList &L)
{
L.elem=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem)
{
printf("开辟空间失败");
exit(0);
}
L.length=0;
L.listsize=LIST_INIT_SIZE;
return OK;
}
//顺序表的插入
Status ListInsert(SqList&L,int i,ElemType e)
{
ElemType*newbase;
int j;
if(i<1||i>L.length+1)
{
printf("插入位置出错");
exit(ERROR);
}
if(L.length>=L.listsize)
{
newbase=(ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(int));
if(!newbase)
{
printf("空间不足");
exit(OVERFLOW);
}
L.elem=newbase;
L.listsize+=LISTINCREMENT;
}
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j];
L.elem[i-1]=e;
L.length+=1;
return true;
}
//顺序表的打印
void ListPrint_Sq(SqList L)
{
ElemType *p = L.elem;
for(int i = 0; i < L.length; i++)
{
printf("%d ", *(p+i));
}
}
Status compare(ElemType a,ElemType b)
{
if(a<=b) return OK;
else return ERROR;
}
//顺序表确定关键字的位置
Status LocateElem(SqList &L,Status e)
{
int i=0;
for(i = 0; i < L.length; i++)
{
if(compare(e,L.elem[i])) break ;
}
return i+1;
}
//排序
void rank(SqList &L)
{
for(int i=0;i<L.length;i++)
scanf("%d",&L.elem[i]);
for(int i=0;i<L.length-1;i++)
for(int j=0;j<L.length-i-1;j++)
if(L.elem[j]>=L.elem[j+1])
{int t=L.elem[j];L.elem[j]=L.elem[j+1];L.elem[j+1]=t;}
printf("非递减排序后的顺序表");
ListPrint_Sq(L);
}
//有序顺序表的合并
void MergeList_Sq(SqList La,SqList Lb,SqList &Lc)
{
ElemType* pa=La.elem,*pb=Lb.elem,*pc,*la,*lb;
Lc.listsize=Lc.length=La.length+Lb.length;
pc=Lc.elem=(ElemType*)malloc(Lc.length*sizeof(ElemType));
if(!Lc.elem) exit(ERROR);
la=La.elem+La.length-1;
lb=Lb.elem+Lb.length-1;
while(pa<=la&&pb<=lb)
{
if(*pa<=*pb) *pc++=*pa++;
else *pc++=*pb++;
}
while(pa<=la) *pc++=*pa++;
while(pb<=lb) *pc++=*pb++;
}
int main()
{
SqList L1,L2,Lc;
int position,t;
ElemType element;
InitList_Sq(L1);
printf("输入顺序表长度");
scanf("%d",&L1.length);
printf("输入顺序表元素");
rank(L1);
InitList_Sq(L2);
printf("输入顺序表长度");
scanf("%d",&L2.length);
printf("输入顺序表元素");
rank(L2);
InitList_Sq(Lc);
MergeList_Sq(L1,L2,Lc);
ListPrint_Sq(Lc);
free(L1.elem);
free(L2.elem);
free(Lc.elem);
return 0;
}
2.链表的基本操作
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#define TRUE 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
typedef struct LNode{
ElemType data;
LNode *next;
}LNode,*LinkList;
typedef struct{
LinkList head,tail;
int len;
}Link;
//链表的建立(正序)
void CreatList_L(Link &L)//注意p++和p=p->next
{
int i,n;
LinkList p;
printf("请输入链表长度");
scanf("%d",&n);
while(n<0){
printf("输入数据有误,请重新输入");
scanf("%d",&n);
}
L.len=n;
L.head=(LinkList)malloc(sizeof(LNode));
if(L.head==NULL) exit(ERROR);
L.head->next=NULL;
for(i=n;i>0;i--){
p=(LinkList)malloc(sizeof(LNode));
if(i==n) {L.tail=p;}
printf("请输入数据");
scanf("%d",&(p->data));
p->next=L.head->next;L.head->next=p;
}
}
//l链表的建立逆序
void CreatList_F(Link &L)
{
int i,n;
LinkList p,q;
printf("请输入链表长度");
scanf("%d",&n);
while(n<0){
printf("输入数据有误,请重新输入");
scanf("%d",&n);
}
L.head=(LinkList)malloc(sizeof(LNode));
if(L.head==NULL) exit(ERROR);
for(i=1;i<=n;i++){
p=(LinkList)malloc(sizeof(LNode));
printf("请输入数据");
scanf("%d",&p->data);
if(i==1) {
L.head->next=p; q=p;}
else {
q->next=p;q=p;}
}
p->next=NULL;
L.tail=p;
}
LinkList LocatePos(Link L,int i)//返回线性表中第i个节点的位置
{
if(i<0||i>L.len+1) exit(ERROR);
LinkList p=L.head->next;
for(int k=1;k<i;k++) p=p->next;
return p;
}
void InsFirst(Link &L,LinkList p,LinkList s)
{
s->next=p->next;
p->next=s;
L.len++;
}
void DelList_L(Link &L,int i)
{
LinkList p= LocatePos( L,i-1);
LinkList q=p->next;
p->next=q->next;
free(q);
}
//min和max范围内的节点删除
void DelList(Link &L,ElemType min,ElemType max)
{
LinkList p=L.head->next,q,r;
for(;p->next->next!=NULL&&p->next->data<=min;)
p=p->next;
if(p->next->next!=NULL&&p->next->data>min)
{for(q=p->next;q->next!=NULL&&q->data<max;)
q=q->next;
for(r=p->next->next;r!=q->next;r=r->next)
{free(p->next);
p->next=r;
}}
else
{
if(p->next->data>min){free(p->next); p->next=NULL;}
}
}
void PrintList(Link L)
{
LinkList p=L.head->next;
if(p==NULL) printf("链表为空");
else{
while(p!=NULL){
printf(" %d",p->data);
p=p->next;}
}
}
int main()
{
Link L;
CreatList_L(L);
DelList(L,1,3);
PrintList(L);
for(LinkList p=L.head->next;L.head!=NULL;p=p->next)
{
free(L.head);
L.head=p;
}
return 0;
}
/*int main()
{
Link L;
CreatList_L(L);
PrintList(L);
for(LinkList p=L.head->next;L.head!=NULL;p=p->next)
{
free(L.head);
L.head=p;
}
return 0;
}
*/