【数据结构(C语言)】数据结构-表
数据结构-表
- 【知识索引】【数据结构(C语言)】
- 一、线性表
- (1)基本概念
- 1.定义(性质相同的数据元素构成的有限序列)
- 2.表长,空表,位序,直接前驱,直接后继
- 3.基本操作(12)
- (2)存储结构
- 1.顺序表
- 2.链表
- 二、栈和队列
- (1)栈
- 1.基本概念
- 2.存储结构
- 3.栈的应用
- (2)队列
- 1.基本概念
- 2.存储结构
- 三、串
- (1)基本概念及术语
- 1.概念
- 2.ADT
- (2)基本操作
- 1.最小操作子集
- 2.其他操作
- (3)串的表示方法
- 1.顺序存储结构
- 2.链式存储结构
- (3)串的应用
- 1.文本编辑
- 四、数组和广义表
- (1)数组
- 1.数组的定义
- 2.数组的顺序表示
- 3.矩阵的压缩存储
- (2)广义表
- 1.定义
- 文章索引
【知识索引】【数据结构(C语言)】
一、线性表
(1)基本概念
1.定义(性质相同的数据元素构成的有限序列)
2.表长,空表,位序,直接前驱,直接后继
3.基本操作(12)
InitList(&L); //构造一个空的线性表L ListEmpty(L);
//判断线性表L是否是空表,若是,则返回TRUE,否则返回FALSE ListLength(L); //返回线性表L的长度
GetElem(L,i,&e) ; //用e返回线性表L的第i个数据元素的值
LocateElem(L,e,compare());//在线性表L中查找第一个和元素e满足compare关系//的元素,若找到则返回其位序;否则返回0
PriorElem(L,e, &pre_e); //用pre_e返回线性表L中元素e的直接前驱 NextElem(L, e,
&next_e); //用next_e返回线性表L中元素e的直接后继 ListInsert(&L,i,e) ;
//将数据元素e插入到线性表L的第i个数据元素之前 ListDelete(&L,i,&e);
//删除线性表L的第i个数据元素,并将其值用e返回 ListTraverse(L,visit());
//依次对线性表L中的每个元素调用visit进行访问 ClearList(&L); //重置线性表L为空表
DestroyList(&L); //销毁线性表L,可能的话释放其空间
(2)存储结构
1.顺序表
用一组地址连续的存储单元依次存储线性表的数据元素。用顺序存储结构表示的线性表又称为顺序表。
- 存储类型定义
#define LIST_INIT_SIZE 100 //顺序表存储空间的初始分配量#define LISTINCREMENT 10 //顺序表存储空间的分配增量typedef struct{ ElemType *elem; //存储空间的基地址int length; //顺序表的当前长度int listsize; //数组存储空间的长度}SqList;
特点
- 逻辑上相邻的数据元素在物理位置上也相邻
- 随机存取
基本操作
初始化顺序表
Status InitSqList(SqList& L) //顺序表初始化 {L.elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType));//申请初始分配空间 if (L.elem == NULL) exit(OVERFLOW);//分配失败,返回OVERFLOW L.length = 0;//初始化表长为零 L.listsize = LIST_INIT_SIZE;//初始化数组存储空间为初始分配空间 return OK;//分配成功返回OK }
销毁顺序表
void DestroySqList(SqList& L)//销毁顺序表 {if (L.elem != NULL)//如果顺序表存在free(L.elem);//释放存储空间 L.elem = NULL;//存储空间基址置空 }
顺序表判空
Status SqListEmpty(SqList L)//顺序表的判空操作 {if (L.length == 0) return TRUE;//表长为零为空表 else return FALSE;//表长非零不为空 }
顺序表求表长
int SqListLength(SqList L)//顺序表求表长 {return L.length; }
获取元素
Status GetSqElem(SqList L, int i, ElemType& e)//取第i个元素,用第三个参数e返回 {if (i<1 || i>L.length)//判断索引是否有效return ERROR;//无效报错 else {e = L.elem[i - 1];//有效则返回元素return OK; } }
定位操作
int SqLocateElem(SqList L, ElemType e)//定位元素e,返回元素位序,若元素不存在则返回零 {int i; for (i = 1; i <= L.length; i++)//遍历数组 {if (L.elem[i - 1] == e);//找到元素跳出循环break; } if (i <= L.length)//判断是否溢出return i; elsereturn 0; }
插入操作
Status SqListInsert(SqList& L, int i, ElemType e)//顺序表插入操作,在位序i上插入元素e {if (i<1 || i>L.length)//判断位序是否有效return ERROR; if (L.length == L.listsize)//判断是否溢出,如果溢出,重新分配空间 {L.elem = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(ElemType));if (!L.elem) exit(OVERFLOW);L.listsize += LISTINCREMENT; } ElemType* q; q = &L.elem[i - 1]; for (ElemType* p = &L.elem[L.length - 1]; p >= q; p--)//从后向前移动元素 {*(p + 1) = *p; } *q = e; L.length++;//更新表长 return OK; }
删除操作
Status SqListDelete(SqList& L, int i)//删除顺序表第i个元素 {if (i<1 || i>L.length) //判断参数是否溢出return ERROR; int j; for (j = i; j < L.length; j++)//从第i+1个元素开始到最后一个元素遍历 {L.elem[j - 1] = L.elem[j];//每个元素向前移 } L.length--; return OK; }
遍历操作
Status SqListTraverse(SqList L, Status(*visit)(ElemType))//顺序表遍历操作 {int i; for (i = 0; i < L.length; i++)//循环遍历顺序表if (visit(L.elem[i])==FALSE) //遍历异常退出return ERROR; return OK; } 定义比较函数: Status equal(ElemType a,ElemType b){if(a==b) return TRUE;else return FALSE; }
优缺点
- 优点:可以随机存取表中任一元素; 大部分操作都比较容易实现;存储利用率高(关系隐含在存储结构里,不需显式表示)
- 缺点:不知道表的规模的情况下,2个常量的大小不太好确定;顺序表在进行插入和删除操作时,需要移动大量元素,浪费大量时间。
顺序表应用
把2个非递减有序的顺序表LA和LB归并为非递减有序的线性表LC
void MergeList_Sq(SqList la,SqList lb,SqList &lc){//算法2.7 pa=la.elem; pb=lb.elem; lc.listsize=lc.length=la.length+lb.length; pc=lc.elem = new ElemType[lc.listsize]; pa_last=la.elem+la.length-1; pb_last=lb.elem+lb.length-1; while(pa<=pa_last && pb<=pb_last) {if(*pa<=*pb) *pc++=*pa++; else *pc++=*pb++; } while(pa<=pa_last) *pc++=*pa++; while(pb<=pb_last) *pc++=*pb++; }
顺序表的就地逆置
void reverse1(Sqlist &A) {for(i=0,j=A.length-1;i<j;i++,j--)A.elem[i]<->A.elem[j]; }//reverse1
2.链表
用一组任意的存储单元(可连续可不连续)存储线性表中的数据元素。
单链表
线性单链表:n个结点(每个结点只包含一个指针域)链接而成的链表。
存储类型定义
typedef struct LNode {ElemType data; //数据域 struct LNode* next; //指针域 }LNode, * LinkList;
特点
- 任何两个元素的存储位置没有固定的联系
- 每个元素的存储位置只能由其直接前驱结点的指针指出
- 链表是顺序存取的存储结构
特殊节点
- 头结点
- 首元结点
- 尾元结点
基本操作
初始化单链表链表
Status InitLinkList(LinkList& L) //初始化链表 {L = (LinkList)malloc(sizeof(LNode));//申请头节点 if (L == NULL) exit(OVERFLOW);//申请失败返回异常 L->next = NULL;//头节点置空 return OK; }
单链表的判空操作
Status LinkListEmpty(LinkList L) //单链表的判空操作 {if (L->next == NULL)//判断头指针是否为空return TRUE; else return FALSE; }
销毁单链表
void DestroyLinkList(LinkList& L) //销毁单链表 {LinkList p;//新建节点指针 while (L != NULL)//头指针非空 {p = L->next;//p指针保留将要释放的节点指针域free(L);//释放节点L = p;//更新头节点 } }
单链表求表长
int LinkListLength(LinkList L) //单链表求表长操作 {LinkList p; int n = 0; for (p = L->next; p != NULL; p = p->next, n++);//从首元节点遍历单链表并计数 return n; }
清空单链表
void ClearLinkList(LinkList L) //清空单链表 {LinkList p; for (p = L->next; p != NULL; p = L->next)//从首元节点遍历释放节点空间 {L->next = p->next;//更新头节点指针free(p);//释放首元节点 } }
单链表取值
Status GetLinkElem(LinkList L, int i, ElemType& e) //单链表取第i个值,由e返回 {int count;//计数变量 LinkList p = L;//遍历指针初始为头指针 for (count = 0; count < i && p != NULL; count++)//计数器置零,遍历到第i个,指针为空异常退出 {p = p->next; } if (i > count)//判断是否异常退出return ERROR; else//正常退出返回OK {e = p->data;return OK; } }
单链表定位
LinkList LinkLocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) //单链表定位操作,返回第一个满足关系的节点指针,若无满足关系的元素,返回空指针 {LinkList p = L->next;//从首元节点遍历链表 while (p!=NULL && compare(p->data, e)==FALSE) //直到满足关系或遍历结束停止遍历p = p->next; return p;//返回指针 }
单链表插入操作
Status LinkListInsert(LinkList& L, int i, ElemType e)//单链表插入操作 {int count;//计数变量 LinkList p;//循环指针 for (count = 0, p = L; count < i - 1 && p != NULL; p = p->next, count++);//定位第i-1个节点 if (i < 0 || count < i - 1) //判断i取值是否合理return ERROR; else {LinkList q;q = (LinkList)malloc(sizeof(LNode));//创建待插入节点空间q->data = e;q->next = p->next;//插入节点指向其后继p->next = q;//连接其前驱节点 } return OK; }
单链表删除操作
Status LinkListDelete(LinkList& L, int i) {int count;//计数变量 LinkList p;//循环指针 for (count = 0, p = L; count < i - 1 && p != NULL; p = p->next, count++);//定位第i-1个节点 if (i < 0 || count < i - 1) //判断i取值是否合理return ERROR; else {LinkList q = p->next;//指向待删除节点p->next = q->next;//前驱节点指针域更新free(q);//删除节点 } return OK; }
遍历单链表
Status LinkListTraverse(LinkList L,Status(*visit)(ElemType)) //遍历单链表 {LinkList p; for (p = L->next; p; p = p->next)if (!visit(p->data)) return ERROR; return OK; }
应用
逆向建立n个元素的单链表
void CreateList_backward(LinkList &L,int n) { //逆向建立n个元素的单链表 L=( LinkList) malloc(sizeof(LNode)); L->next=NULL; for(i=0; i<n; i++) { p=( LinkList) malloc(sizeof(LNode));scanf(&p->data); p->next=L->next; L->next=p; }//for }// CreateList_backward
优缺点
- 优点:链表在进行插入和删除操作时,不再移动大量元素,仅需修改指针;不再需要定义2个常量
- 缺点:元素只能顺序存取;大部分操作的时间复杂度都为O(n);存储利用率较低(存储元素时要附带存储指针)
循环链表
优点:从表中任一结点出发都可找到表中其他结点。
操作
和线性链表基本一致,差别在于判空和判表尾条件
判空: L->next==L
表尾:p->next==L
在带头结点的循环链表中设置尾指针可方便某些操作
- 查找尾元结点
- 查找首元结点
应用
两个循环链表合并
仅设尾指针的循环链表
p=LA->next;
LA->next=LB->next->next;
free(LB->next);
LB->next=p;
LA=LB;
双向链表
双向链表C语言描述
typedef struct DulNode{ElemType data; Struct DulNode *prior;//指向其直接前驱的指针域 Struct DulNode *next; //指向其直接后继的指针域 }DulNode,*DuLinkList;
操作
某些操作算法与线性单链表的操作相同;
插入、删除等操作需同时修改两个方向的指针插入
Status ListInsert_Dul(DuLinklist &L,int i,ElemType e) {if(!(p=GetElem_DuL(L,i))) return ERROR; if(!(s=(DuLinkList)malloc(sizeof(DuLNode)))) return ERROR; s->data=e; ① s->prior=p->prior; ② p->prior->next=s; ③ s->next=p; ④ p->prior=s; return OK; }
删除
Status ListDelete_Dul(DuLinklist &L,int i,ElemType &e) {if(!(p=GetElem_DuL(L,i))) return ERROR; e=p->data; ① p->prior->next=p->next ; ② p->next->prior=p->prior; ③ free(p); return OK; }
二、栈和队列
(1)栈
1.基本概念
栈:限制仅在表的一端进行插入和删除的线性表
ADT定义
ADT Stack { 数据对象:D={ai| ai ElemSet, i=1,2,…,n, n0} 数据关系:R={<
ai-1, ai >| ai-1,ai D,i=2,…,n}
约定an端为栈顶, a1端为栈底 基本操作:InitStack(&S); StackEmpty(S);
StackLength(&S); GetTop(S, &e);
Push(&S, e); Pop(&S, &e);
ClearStack(&S);
StackTraverse(S, visit());
DestroyStack(&S); } ADT Stack栈顶:允许插入、删除的一端。
栈底:与栈顶相对的另一端。
空栈:表中没有元素的栈
2.存储结构
顺序栈
特点:用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素
栈中元素可动态增删,即栈长是可变的,因此和顺序表一样,用动态分配的一维数组来表示顺序栈存储结构的定义
#define STACK_INIT_SIZE 100 //顺序栈存储空间的初始分配量 #define STACKINCREMENT 10 //顺序栈存储空间的分配增量typedef struct {SElemType* base; //栈底指针,始终指示存储空间的基址 SElemType* top; //栈顶指针,指向栈顶元素的下一个位置 int stacksize; //数组存储空间的长度 }SqStack;//顺序栈定义
- 栈底指针,始终指示存储空间的基址
- 栈顶指针,指向栈顶元素的下一个位置
- 数组存储空间的长度
基本操作
构造空栈
Status InitSqStack(SqStack& S) { S.base = (SElemType*)malloc((STACK_INIT_SIZE) * sizeof(SElemType));// 栈的连续空间分配 if (!S.base) {exit(OVERFLOW); } S.top = S.base; //空栈,初始化栈顶指针 S.stacksize = STACK_INIT_SIZE; return OK; }//构造一个空栈,该栈由指针S指示
销毁栈
Status DestroySqStack(SqStack& S) {if (S.base) //栈底指针非空free(S.base); S.base = NULL; return OK; }//销毁栈
清空栈
Status ClearSqStack(SqStack& S) {S.top = S.base; return OK; }//清除栈
判空
Status SqStackEmpty(SqStack S) {return S.top == S.base; }//栈的判空操作
求栈长
int SqStackLength(SqStack S) {return S.top - S.base; }//求栈长
取栈顶元素
Status GetTopSq(SqStack S, SElemType& e) {if (S.top == S.base) return ERROR; e = *(S.top - 1); return OK; } //用指针e带回栈顶元素
入栈
Status SqPush(SqStack& S, SElemType e) {//入栈,插入元素e为新的栈顶元素 if (S.top - S.base == S.stacksize)//栈满 {S.base = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));if (!S.base) exit(OVERFLOW);S.top = S.base + S.stacksize;S.stacksize += STACKINCREMENT; }//重新分配更大的连续空间 *S.top++ = e; return OK; }//Push
出栈
Status SqPop(SqStack& S, SElemType& e) {if (S.top == S.base) //栈空return ERROR; --S.top; e = *S.top; return OK; }//出栈,删除的栈顶元素用指针e指示
遍历
Status SqStackTraverse(SqStack S, Status(*visit)(SElemType)) {//从栈底到栈顶依次对栈S中的每个数据元素调用visit //指向的函数进行访问 SElemType* p; for (p = S.base; p < S.top; p++)if (!visit(*p)) return ERROR; return OK; }// 栈的遍历
链栈
链栈类型定义
typedef struct SNode{SElemType data; //数据域 struct SNode *next; //指针域 }SNode,*LinkStack;
- 由于栈顶的位置特殊,所以链栈的头指针指向栈顶元素所在结点
- 注意:不必设头结点
基本操作
链栈入栈
Status Push(LinkStack &S, SElemType e) {//插入e到栈S中使之成为新的栈顶元素 p=( LinkStack)malloc(sizeof(SNode)); if(!p) exit(OVERFLOW); p->data=e; p->next=S; S=p; return OK; }//Push
链栈出栈
Status Pop(LinkStack &S, SElemType &e) {//若栈不空则出栈,删除的栈顶元素用e指示 if(!S) return ERROR; //栈空 p=S; S=S->next; e=p->data; free(p); return OK; }//Pop
3.栈的应用
数制转换
括号匹配的检验
迷宫求解
Typedef struct { //栈的元素类型定义int ord; //通道块在路径上的“序号”PosTpye seat ; //通道块在迷宫中的“坐标位置”int di; //从此通道块走向下一通道块的“方向” }SElemType; Status MazePath(MazeType maze, PosType start, PosType end){InitStack(s); curpos=start; //设定当前位置为入口位置curstep =1;do{ if(Pass(curpos)) { //当前位置可通FootPrint(curpos); //留下足迹e=(curstep, curpos, 1);push(s,e); //加入路径if(curpos==end) return(TRUE); //到达终点curpos= NextPos(curpos, 1); //下一位置是当前位置的东邻curstep++; //探索下一步}//ifelse { //当前位置不能通过if (!StackEmpty(S)) { Pop(S,e); while(e.di==4 && !StackEmpty(s)){MarkPrint(e.seat); Pop(s,e); //留下不能//通过的标记,并退回一步 }//while if (e.di<4){e.di++; Push(S,e); //换下一方向搜索curpos=NextPos(e.seat, e.di);//设定当前位置是该新方向上的相邻块 }//if }//if }//else } while(!StackEmpty(S)); return(FALSE); }// MazePath
算术表达式求值
OperandType EvaluateExpression(){InitStack(OPTR); Push(OPTR, '#'); InitStack(OPND); c=getchar(); while(c!= '#' || GetTop(OPTR)!= '#'){if (!In(c,OP)) {Push(OPND,c); c=getchar();} else switch(Precede(GetTop(OPTR),c)){case '<': Push(OPTR,c); c=getchar(); break;case '=': Pop(OPTR,x); c=getchar(); break; case '>': Pop(OPTR,theta); Pop(OPND,b); Pop(OPND,a);Push(OPND,Operate(a,theta,b)); break; }//switch }//while return GetTop(OPND); }// EvaluateExpression
栈与递归
(2)队列
1.基本概念
队列:只允许在表的一端进行插入,而在另一端进行删除的线性表。
ADT
ADT Queue {
数据对象:D={ai | ai ElemSet, i=1,2,…,n, n>=0}
数据关系:R={< ai-1, ai > | ai-1, ai D, i=2,3,…,n}
约定a1端为队头,an端为队尾
基本操作:InitQueue(&Q); QueueEmpty(Q);
QueueLength(Q); GetHead(Q, &e);
EnQueue(&Q, e); DeQueue(&Q, &e);
ClearQueue(&Q);
QueueTraverse(Q, Visit());
DestroyQueue(&Q); }ADT Queue队头:允许删除的一端
队尾:允许插入的一端
空队列:没有元素的队列
双端队列:限定插入和删除操作在表两端进行的线性表
输出受限队列:一端允许插入和删除,另一端只允许插入。
输入受限队列:一端允许插入和删除,另一端只允许删除。
2.存储结构
链队列
使用二个指针分别记录队头和队尾的当前位置
设立一个头结点,并令头指针指向头结点,头结点的指针域指向队头元素所在的结点
链队列的判空条件:头指针和尾指针均指向头结点链队列类型定义
typedef struct QNode{QElemType data; struct QNode *next; }QNode,*QueuePtr; typedef struct {QueuePtr front; //头指针 QueuePtr rear; //尾指针 }LinkQueue;```
基本操作
链队列入队
Status EnQueue(LinkQueue &Q, ElemType e) { p=(QueuePtr)malloc(sizeof(QNode)); if(!p) exit(OVERFLOW); p->data=e; p->next=NULL; Q.rear->next=p; Q.rear=p; //尾指针指向新的尾结点 return OK; }// EnQueue
链队列入出队
Status DeQueue(LinkQueue &Q, ElemType &e) {if(Q.rear == Q.front) return ERROR; p=Q.front->next; e=p->data; Q.front->next=p->next; if(Q.rear==p) Q.rear=Q.front; //若队头元素是队列中唯一的一个元素, 则删除该结点后//还需要修改尾指针,让它指向头结点 free(p); return OK; } // DeQueue
顺序队列
普通顺序队列
用一组地址连续的存储单元依次存储从队头到队尾的数据元素。
顺序队列类型定义
#define MAXQSIZE 100 typedef struct {QElemType *base; //存储空间基地址 int front;//队头指针,指向队头元素 int rear;//队尾指针,指向队尾元素的下一个位置 }SqQueue;
假上溢现象
循环队列
存储结构定义
少用一个元素空间,约定队列头指针在队尾指针下一个位置为队列满
#define MAXQSIZE 100 typedef int QElemType; typedef struct {QElemType* base; //存储空间基地址 int front;//队头指针,指向队头元素 int rear;//队尾指针,指向队尾元素的下一个位置 }SqQueue;
另设标志位区别队列空、满
typedef struct {ElemType *base; int front; int rear; int tag; }SqQueue;
基本操作
初始化队列
Status InitSqQueue(SqQueue& Q)//初始化队列 {Q.base = (QElemType*)malloc(MAXQSIZE * sizeof(QElemType)); if (Q.base == NULL)exit(OVERFLOW); Q.front = Q.rear = 0;//队头队尾指针都指向0 return OK; }
求队长
int SqQueueLength(SqQueue Q)//求队长 {return(Q.rear - Q.front + MAXQSIZE) % MAXQSIZE; }
进队
Status EnSqQueue(SqQueue& Q, QElemType e)//进队操作 {if ((Q.rear + 1) % MAXQSIZE == Q.front) //队列满的判定条件(防止队满和队空无法区分,留下一个位置不用return ERROR; //队满直接报错,不再申请更大的内存 else {Q.base[Q.rear] = e;Q.rear = (Q.rear + 1) % MAXQSIZE;//队尾指针后移,%MAXQSIZE使下标首尾相接return OK; } }
出队
Status DeQueue(SqQueue& Q, QElemType& e)//出队操作 {if (Q.front == Q.rear) //队空的判定条件return ERROR; //队空报错 else {e = Q.base[Q.front];Q.front = (Q.front + 1) % MAXQSIZE;return OK; } }
判空
Status SqQueueEmpty(SqQueue Q)//循环队列的判空操作 {if (Q.rear == Q.front)return TRUE; elsereturn FALSE; }
取队头元素
Status GetSqHead(SqQueue Q, QElemType& e)//取队头元素 {if (Q.rear == Q.front)return ERROR; else {e = Q.base[Q.front];return TRUE; } }
清空队列
Status ClearSqQueue(SqQueue& Q)//清空队列 {Q.rear = Q.front; return OK; }
销毁队列
Status DestroySqQueue(SqQueue& Q)//销毁队列 {free(Q.base); Q.front = Q.rear = 0; return OK; }
队列的遍历
Status SqQueueTraverse(SqQueue Q, Status (*visit)(QElemType)) { int i; for (i = Q.front; i != Q.rear; i = (i + 1) % MAXQSIZE) {visit(Q.base[i]);} return OK; }
三、串
(1)基本概念及术语
1.概念
串:
由零个或多个字符组成的有限序列。记为:s= ‘a1 a2…an’ (n>=0)
串名: s
串值: a1a2a3……an
串长: n
空串:串长为0的串。用Φ表示
子串:串中任意个连续的字符组成的子序列
- 串是其自身的子串
- 空串是任意串的子串
主串:包含子串的串
字符在串中的位置:字符在串中的序号
子串在主串中的位置:子串的第1个字符在主串中的位置
空格串:由一个或多个空格组成的串,其长度为空格个数
串相等:两个串长度相等且各个对应位置的字符也相等
2.ADT
ADT String { 数据对象:D={ai| ai CharacterSet, i=1,2,…,n, n0}
数据关系:R1={< ai-1 ,ai >| ai-1 , ai D,i=2,3,…,n} 基本操作:
StrAssign(&T, chars); StrCopy(&T,S);
StrEmpty(S); StrCompare(S,T);
StrLength(S); ClearString(&S);
Concat(&T,S1,S2); Substring(&Sub, S, pos,len);
Index(S,T,pos); Replace(&S,T,V);
StrInsert(&S, pos, T); StrDelete(&S, pos, len);
DestroyString(&S); }ADT String;
(2)基本操作
1.最小操作子集
- 串赋值StrAssign
- 串比较StrCompare
- 求串长StrLength
- 串联接Concat
- 求子串SubString
2.其他操作
- 串拷贝StrCopy
- 串判空StrEmpty
- 定位串Index
- 串替换Replace
- 串插入StrInsert
- 串删除StrDelete
- 串清空ClearString
- 销毁串DestroyStr
(3)串的表示方法
1.顺序存储结构
定长存储结构
- 特点:用长度固定的连续单元依次存储串值的字符序列
- 以下标为0的数组分量存放实际串长——PASCAL
- 串值后加一个不计入串长的结束标记字符——C、C++中用‘\0’作串的结束标记
堆分配存储结构
特点:采用动态字符数组存放串值,此时不必为数组预定义大小,以串长动态分配数组空间
数据类型定义
typedef struct {char *ch; int length; }HString;
2.链式存储结构
块链结构
数据类型定义
#define CHUNKSIZE 80 // 可由用户定义的块大小 typedef struct Chunk { // 结点结构 char ch[CUNKSIZE]; struct Chunk *next; } Chunk; typedef struct { // 串的链表结构Chunk *head, *tail; //串的头和尾指针,便于联结操作int curlen; // 串的当前长度 } LString;
(3)串的应用
1.文本编辑
- 实质:修改字符数据的形式或格式
- 基本操作:输入、查找、修改、删除、输出
四、数组和广义表
(1)数组
1.数组的定义
ADT
ADT Array { 数据对象:ji=0, …, bi – 1, i = 1, 2, …, n,
D={ | ∈ElemSet } 数据关系:R = {R1, R2, …, Rn}
Ri={ |0 ≤ jk ≤ bk–1, 1 ≤ k ≤n且
k!=i, 0 ≤ ji ≤ bi–2, ∈D, i=1,2,…,n} 基本操作: InitArray(&A, n, bound1, …, boundn) DestroyArray(&A)
Value(A, &e, index1, …, indexn) Assign(&A, e, index1, …, indexn)
}ADT Arrayn维数组是线性表的扩展
- 当n=1,n维数组退化成顺序表
- 当n>1,n维数组可看成表中数据元素是n-1维数组的线性表
2.数组的顺序表示
因为数组一般不做插入/删除操作,所以用顺序结构表示数组是很自然的。
特点:用一组地址连续的存储单元按照某种规则存放数组中的数据元素。
2种顺序(顺序存储方式)
- 以行序为主(低下标优先法)
- 以列序为主(高下标优先法)
元素地址的计算
要素
- ①数组的起始地址(即基地址)
- ②数组维数和各维的长度;
- ③数组中每个元素所占的存储单元
计算方法
二维
行主序:LOC(i,j) = LOC(0,0) + ( b2*i + j ) * L
列主序:LOC(i,j) = LOC(0,0) + ( b1*j + i ) * L
二维数组元素地址的通式
行优先存储时的地址公式为:
- LOC(aij)=LOC(ac1,c2)+[(i-c1)*(d2-c2+1)+j-c2)]*L
列优先存储的通式为:
- LOC(aij)=LOC(ac1,c2)+[(j-c2)*(d1-c1+1)+i-c1)]*L
多维
- 设各维长度分别为b1, b2, b3, …, bn,每个元素占L个存储单元, 起始地址是LOC(0,0,…,0)
- LOC (j1,j2,…,jn ) = LOC(0,0,…,0) + (b2b3…bn * j1+ b3b4*…bn * j2+ ……+ bnjn-1 + jn ) * L
随机存储结构
数组元素的存储位置是其下标的线性函数,由于计算各个元素存储位置的时间相等,所以存储数组中任一元素的时间也相等,具有这一特点的存储结构为随机存储结构。
实现
#define MAX_ARRAY_DIM 8 typedef struct {ElemType *base; //存储空间基址 int dim; //数组维数 int *bounds; //数组维界基址 int *constants; //数组映象函数常量{Ci}基址 } Array;
3.矩阵的压缩存储
压缩存储:为多个值相同的元素只分配一个存储空间,对零元素不分配空间。
特殊矩阵:值相同的元素或零元素在矩阵中分布有一定规律。如三角矩阵、对角矩阵。
对称矩阵
n阶矩阵A中元素满足性质a[i][j]=a[j][i] (1 ≤i, j≤ n)
压缩存储
为每一对对称元素分配 个存储空间. n2 n(n+1)/2
用行主序的下(上)三角阵来存储对称矩阵的元素。
sa[k](0 ≤ k ≤ n(n+1)/2-1) 为对称矩阵的压缩存储结构
上(下)三角阵
下(上)三角(不含对角线)元素为常数c或0的n阶矩阵
压缩存储
存储上(下)三角中的元素和常数c
用行主序存储上(下)三角阵的元素
sak 为上(下)三角阵的压缩存储结构
对角矩阵
所有非零元素都集中在以主对角线为中心的带状区域。其他元素为0。
压缩存储
用行主序存储带状区域的非0元素
稀疏矩阵:值相同的元素或零元素在矩阵中分布没有一定规律。
稀疏因子
设m行n列的矩阵含t个非零元素,定义δ=t/(m*n)为稀疏因子,则 0.05 的矩阵为稀疏矩阵。
稀疏矩阵的压缩存储
表示
三元组( i,j,aij )
三元组的C语言描述
typedef struct {int i,j;ElemType e; }Triple;
三元组顺序表的C语言描述
#define MAXSIZE 1250 typedef struct{Triple data[MAXSIZE+1]; //data[0]未用int mu,nu,tu; }TSMatrix;
行逻辑链接的顺序表
需求:随机存取任意一行的非0元
方法:记住矩阵每一行第一个非0元在三元组顺序表中的位置
设数组rpos[1…n]:矩阵中每行第一个非零元素的起始位置
rpos[1]=1;
rpos[row]=rpos[row-1]+第row-1行的非零元素个数
第i行所有非0元在data中的位置:rpos[i]…rpos[i+1]-1
行逻辑链接顺序表:在稀疏矩阵的三元组顺序表中设置指示行信息的辅助数组rpos
typedef struct{
Triple data[MAXSIZE+1];
int rpos[MAXRC+1];//各行第1个非零元位置表,rpos[0]未用
int mu,nu,tu;
}RLSMatrix;十字链表
转置
稀疏矩阵转置方法一
Status TransposeSMatrix(TSMatrix M,TSMatrix &T) { T.mu=M.nu;T.nu=M.mu;T.tu=M.tu;if(T.tu){ q=1;for(col=1;col<=M.nu;++col)for(p=1;p<=M.tu;++p)if(M.data[p].j==col){T.data[q]=(M.data[p].j, M.data[p].i, M.data[p].e); ++q;} } return OK; }//TransposeSMatrix
稀疏矩阵转置方法二
Status FastTransposeSMatrix(TSMatrix M,TSMatrix &T) { T.mu=M.nu;T.nu=M.mu;T.tu=M.tu; if(T.tu) { for(col=1;col<=M.nu;++col) num[col]=0;for(t=1;t<=M.tu;++t) ++num[M.data[t].j];cpot[1]=1;for(col=2;col<=M.nu;++col) cpot[col]=cpot[col-1]+num[col-1];for(p=1;p<=M.tu;++p) {col=M.data[p].j; q=cpot[col];T.data[q]=( M.data[p].j, M.data[p].i, M.data[p].e ); ++cpot[col]; }//for }//if return OK; }//FastTransposeSMatrix
(2)广义表
1.定义
广义表(列表):n (0)个元素组成的有限序列,记作
LS = (α1, α2, …, αn) 其中αi 可以是单个元素(原子),也可以是广义表(子表)
- 表头(head):n>0时,广义表的第一个元素
- 表尾(tail):除第一个元素外的所有其它元素组成的表
- 空表:n = 0 的广义表
- 广义表长度:广义表中元素的个数
- 广义表深度:广义表中括号的重数
文章索引
- 【知识索引】【数据结构(C语言)】
- 【数据结构(C语言)】数据结构-表
- 【数据结构(C语言)】数据结构-树
- 【数据结构(C语言)】数据结构-图
- 【数据结构(C语言)】数据结构-查找
- 【数据结构(C语言)】数据结构-内部排序
【数据结构(C语言)】数据结构-表相关推荐
- c语言 静态链表插入排序,数据结构C语言版 表插入排序
西门豹治邺奇计 数据结构C语言版 表插入排序.txt两个人吵架,先说对不起的人,并不是认输了,并不是原谅了.他只是比对方更珍惜这份感情./* 数据结构C语言版 表插入排序 算法10.3 P267-P2 ...
- 数据结构C语言——广义表
很久没有发博客记录了,不过应该也没人关心这个,哈哈哈. 突然想起来,学习也不能只是干学,稍微做点记录. 之前我对html.css.js基础有了一定的理解,也学了一些模板,不过现在我想先把计算机基础给补 ...
- c语言编程文件中删除数据结构,C语言数据结构实战(一)顺序表的插入与删除
今天学习了思成老师的数据结构实战教程 写了一个顺序表 插入和删除的操作 把源码共享给大家 一共包括list.c stu.h main.c list.h .h文件是头文件 需要引入 具体的功能我都已 ...
- 数据结构(C语言)顺序表的定义及基本操作
顺序表的定义及基本操作 一.数据结构:顺序表的定义,创建,基本运算,实现 二.全部代码 定义顺序表 #include<stdio.h> #include<malloc.h> # ...
- 数据结构C语言顺序表入门简单题目你会了吗?
通常,线性表可以采取顺序存储和链式存储两种,今天我们来讨论下顺序存储结构以及其对应的实现算法. 采用顺序存储是表示线性表的最简单的方法,具体做法是:将线性表中的元素一个接一个地存储在一片相邻的存储区域 ...
- 栈的应用行编辑数据结构c语言,数据结构题典022:栈的应用——行编辑程序(C语言版)...
编写程序实现从终端接收用户输入的数据,并存入用户数据区.输入#表示上一字符无效,输入@表示当前输入行整行无效. /* * line editor by using stack. * * fduan, ...
- c语言数据结构线性表LA和LB,数据结构(C语言版)设有线性表LA(3,5,8,110)和LB(2,6,8,9,11,15,20)求新集合?...
数据结构(C语言版)设有线性表LA(3,5,8,110)和LB(2,6,8,9,11,15,20)求新集合? 数据结构(C语言版)设有线性表LA(3,5,8,110)和LB(2,6,8,9,11,15 ...
- 逆置单链表c语言程序,(数据结构C语言版)顺序表和单链表的逆置
<(数据结构C语言版)顺序表和单链表的逆置>由会员分享,可在线阅读,更多相关<(数据结构C语言版)顺序表和单链表的逆置(7页珍藏版)>请在人人文库网上搜索. 1.实验1-1顺序 ...
- 数据结构——无向图创建邻接表以及深度遍历、广度遍历(C语言版)
摘自:数据结构--无向图创建邻接表以及深度遍历.广度遍历(C语言版) 作者:正弦定理 发布时间:2020-12-22 20:55:12 网址:https://blog.csdn.net/chinese ...
- 顺序表输入栈元素c语言,C语言数据结构之栈简单操作
C语言数据结构之栈简单操作 实验: 编写一个程序实现顺序栈的各种基本运算,并在此基础上设计一个主程序,完成如下功能: (1)初始化顺序栈 (2)插入元素 (3)删除栈顶元素 (4)取栈顶元素 (5)遍 ...
最新文章
- redis一:非关系型数据库
- 设计模式(二)————观察者模式
- .Net Validator验证框架 [ .Net | Validator Framework | Attribute ]
- Windows Server 2016与旧版本系统比较
- shell获取执行脚本路径
- Linux运维-day3
- chrome本地文件加载跨域请求
- 利用React/anu编写一个弹出层
- Cesium:在地球上加载Geoserver图层
- 为什么要Code Review
- Android Retrofit 2.0 使用-补充篇
- pythonpdf使用教程_Python基础学习教程:Python玩转PDF各种骚操作大全
- 开运算和闭运算的性质
- matlab矩阵排序sort,MATLAB数组元素的排序
- java生成随机数组_Java 生成随机数
- 全国大学生大数据技能竞赛比赛心得以及相关资料
- H5+js实现别踩白块儿
- oracle general ledger,处理 Oracle General Ledger 调整期间
- iOS 设备的屏幕尺寸、分辨率及其屏幕边长比例详细情况
- excel表格如何换行
热门文章
- C++之unique_ptr
- kalilinux装到u盘上的弊端_你有一个 U 盘制作多系统安装盘的需求吗,YUMI 帮你秒实现!...
- php简单选择题,念做个简易php选择题答题系统
- ubuntu18.04安装mysql8.0
- php ext在哪里,PHP Ext API
- python二维列表转字典_在Python中字符串、列表、元组、字典之间的相互转换
- 此流上不支持超时。_10分钟了解线程池,阿里再也不担心我线程池资源耗尽了...
- python列表的排序方法是_Python列表排序 reverse、sort、sorted 操作方法详解
- 低解密指数攻击_CTF中RSA的一些攻击思路
- java序列化和反序列化对象_java中的序列化与反序列化,还包括将多个对象序列化到一个文件中...