2.线性表
2.1.定义、特点:
1.线性表是一种线性结构。
2.特点:1 数据元素之间是线性关系,即“一对一”;2 除第一个元素,每一个元素都有且只有一个前驱元素;3 除最后一个元素,每个元素都有且只有一个后继元素;4.均匀性;5 有序性;
2.1.2.基本操作:
1.置线性表为空:L->len=0;
2.求线性表的长度:L->len的长度;
3.查询线性表中的元素:
4.插入元素;
5.删除元素;
2.2.线性表的顺序存储结构及运算实现:
2.2.1.顺序表:
1.定义:线性表的顺序存储方式使用一组地址连续的存储单元按顺序依次存放线性表中的每一个元素,这种方式也称为顺序表;
2.使用一维数组来存储顺序表最适合;
3.顺序表的结构体:
typedef struct { int data[MAXSIZE]; int len; }SeqList;2.2.2.顺序表的基本操作:
1.顺序表的初始化:需要童泰分配存储空间,并置表长为0:
SeqList *init_SeqList(){ SeqList *L; L=(SeqList *)malloc(sizeof(SeqList)); L->len=0; }
2.建立顺序表:获取表长:
void createList(SeqList *L){ int n,i; puts("请输入表长:"); scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&i); } L->len=n; puts("创建顺序表成功!!"); }
3.插入元素:将n~i的元素依次后移,然后将x放在i处,最后将表长增大1位;
插入时可能出现的情况:1 L->len==MAXSIZE-1,说明表已经存满了;2 i<1||i>L->len,说明存储时不会连续;因此需要条件语句进行判断;
void insertList(SeqList *L,int i,int x){ int j; if(L-len==MAXSIZE-1){ puts("存满!!"); return; }else if(i<1||i>L->len+1){ puts("非法地址!!"); return; }else{ for(j=L->len;j>=i;j--){ L->data[j+1]=L->data[j]; } L->data[i]=x; L->len++; puts("插入成功!!"); } }
4.删除运算:将i+1到n的值依次前移,然后将表长减1;
删除元素时可能出现的情况:1 L->len==0,说明表为空;2 i<0||i>L->len,说明删除元素位置非法;
int deleteList(SeqList *L,int i){ int j; if(L->len==0){ puts("表为空!!"); return 0; }else if(i<1||i>L->len){ puts("非法地址!!"); return 0; }else{ for(j=i+1;j<=L->len;j++) L->data[j-1]=L->data[j]; } return L->len--; }
5.查找运算:从1开始到len依次查找,并返回下标值:
int queryList(SeqList *L,int x){ int i=1; while(i<L->len&&L->data[i]!=x){ i++; } if(L->data[i]==x) return i; else return 0; }
2.3.线性表的链式存储结构及其实现:
2.3.1.单链表:每个结点只有一个指向后继结点的指针;
1.顺序表的优点是可以随机存取表中的任意一个元素,但是缺点是需要移动大量元素,线性表的链式存储可以用连续或者不连续的存储空间来存储线性表中的元素,通过指针链接起各元素;
2.链式存储需要占用额外的空间来存储指针;
3.结点:通过指针建立起元素之间的线性关系,这个元素叫做结点;
4.结点存放数据信息的部分叫做数据域,存放指向后继结点指针的部分叫做指针域;
5.单链表的定义:
typedef struct node{ datatype data; struce node *next; }LNode;
6.特点:1 逻辑关系相邻的元素的存储地址可以不相邻;2 表中的元素只能顺序访问,不能随机访问;3 表的大小可以动态变化;4 插入、删除只需移动指针,不需移动元素;
7.为了便于单链表的建立和运算的统一,使用头指针,即指向第一个结点的指针,它不存储任何数据;
2.3.2.单链表的基本运算:
1.建立单链表:有两种方式,头插法和尾接法;
1.1.头插法:每次插入到头指针的后面,这样生成结点的顺序恰好和线性表中存储的元素相反,如下:
LNode *createLinkList_tou(){ LNode *p,*head; char x; head=(LNode *)malloc(sizeof(LNode)); head->next=NULL; printf("请输入字符:\n"); scanf("&c",&x); while(x!='\n'){ p=(LNode *)malloc(sizeof(LNode)); p->data=x; p->next=head->next; head->next=p; scanf("%c",&x); } return head; }
1.2.尾接法:比起头插法较复杂,将新的结点插入到链尾,因此需要再增加一个指针,如下:
LNode *createLinkList_wei(){ LNode *q,*p,*head; char x; head=(LNode *)malloc(sizeof(LNode)); head->next=NULL; p=head; q=p; printf("请输入字符:\n"); scanf("&c",&x); while(x!='\n'){ p=(LNode *)malloc(sizeof(LNode)); p->data=x; p->next=NULL; q->next=p; q=p; scanf("%c",&x); } return head; }
2.求表长:
int len_LinkList(LNode *L){ int i=0; LNode *p=L; while(p->next!=NULL){ p=p->next; i++; } return i; }
3.查找:分为按序号查找和按值查找,如下:
LNode *queryList_value(LNode *L,int x){ LNode *p=L; while(p->next!=NULL&&p->data!=x){ p=p->next; } return p; } LNode *queryList_index(LNode *L,int i){ LNode *p=L; int j=0; while(p->next!=NULL&&j<i){ p=p->next; j++ } return p; }
4.插入:q->next=p->next;p->next=q;
void insertLinkList(LNode *L,int i,char c){ LNode *p,*q; p=queryList_index(L,i-1); if(p=NULL){ printf("ERROR!!!"); }else{ q=(LNode *)malloc(sizeof(LNode)); q->data=c; q->next=p->next; p->next=q; } }
5.删除:p->next=p->next->next;
void deleteLinkList(LNode *L,int i){ LNode *p,*q; p=queryList_index(L,i-1); if(p==NULL){ printf("ERROR!!!"); }else{ if(p->next=NULL) printf("ERROR!!!"); q=p->next; p->next=q->next; free(q); } }
2.2.3.循环链表:将链尾结点的指针值由NULL改为指向头结点,整个链表形成一个环;
1.在操作循环链表时,不用头指针head,而是使用一个指向链尾的指针R来表示循环链表,这样可以很快的找到链尾元素和头指针,即R->next;
2.将两个循环链表链接成一个循环链表:
P=R1->next;R1->next=R2->next->next;free(R2->next);R2->next=P;
2.2.4.双向链表:
1.相对于单链表来说,循环链表只能从前向后进行遍历,无法直接到达当前节点的前驱节点;因此采用双向链表满足这些需要沿着两个方向遍历的链表;
2.组成:前驱指针域+数据域+后继指针域;
3.定义:
typedef struct dnode { datatype data; struct dnode *prior,*next; }DLNode;
4.假设p是双向链表中的某一节点的指针,则有:p->prior->next=p;p->next->prior=p;
5.双链表的建立:
void DLNode *createDLink(){ DLNode *p,*head; char c; head=(DLNode *)malloc(sizeof(DLNode)); head->next=head; head->prior=head; scanf("%c",&c); while(c!='\n'){ p=(DLNode *)malloc(sizeof(DLNode)); p->data-c; p->prior=head; p->next=head->next; head->next->prior=p; head->next=p; scanf("%c",&c); } return p; }
6.在结点*p后插入结点*s的步骤:
s->prior=p; s->next=p->next; p->next->prior=s; p->next=s;
7.在结点*p前插入结点*s的步骤:
s->next=p; s->prior=p->prior; p->prior->next=s; p->prior=s;
8.删除结点*p的步骤:
p->prior->next=p->next; p->next->prior=p->prior; free(p);
2.2.5.静态链表:有些高级语言没有指针,因此想使用链表时,用一个一维数组来模拟链表,称之为静态链表;
2.定义:用一个一维数组的数组元素表示结点,结点中的数据域用来存放数据,下标域取代指针域;
typedef struce { datatype data; int cursor; }SNode; SNode L[MaxSize];
3.数组中序号为0的元素看成固定的头结点,下标域为0时表示该结点为链表的链尾结点,下标域为-1时表示该结点未使用,这种存储结构要事先分配内存;