2.1线性表的定义和特点
线性表是具有相同特性的数据元素的一个有限序列
n = 0空表
2.3 线性表的类型定义
InitList(&L) 构造一个空的线性表
DestroyList(&L)
ClearList(&L)
ListEmpty(L)
GetElem(L,i,&e) e返回L中第i个元组值
LocateElem(L,e,compare()) 返回第一个与e满足比较的位序
PriorElem(L,cur_e,&pre_e) 返回cur_e的前驱
ListInsert(&L,i,e) 在i位置插入e
ListDelete(&L,i,&e) 删除第i个元素,e返回其值
ListTraverse(&L,visited()) 对表中每个元素调用visited
2.4.1 线性表的顺序存储表示
顺序存储定义:逻辑上相邻,物理上相邻,
LOC(ai) = LOC(a1) + (i - 1)*l
顺序表: 地址连续,随机存取,类型相同,依次存放
线性表长可变,数组长度不可动态定义
#define SIZE 100 //线性表存储空间的初始分配量
数组静态分配: 数组动态分配:
typedef struct{ typedef struct{
ElemType elem[SIZE]; ElemType *elem;
int length;//当前长度 int length;
}SqList; }SqList;
SqList L;//定义变量L,L是SqList 这种类型的,L是个顺序表
L.elem = (ElemType*)malloc (sizeof(ElemType)*SIZE);
多项式的顺序存储结构类型定义
#define MAX 1000
typedef struct{
float p;//系数
int e;//指数
}Polynomial;
typedef struct{
Polynomial *elem;//存储空间的基地址
int length;//多项式中当前项的个数
}SqList;//多项式的顺序存储结构类型为SqList
图书表的顺序存储结构类型定义
#define MAXSIZE 1000
typedef struct{
char no[20];//图书ISBN
char name[50];//图书名字
float price;//图书价格
}Book;
typedef struct{
Book *elem;//存储空间的基地址
int length;//图书表中当前图书个数
}SqList;
Typedef char ElemType;
SqList L;
L. elem = (ElemType*)malloc (sizeof(ElemType)*SIZE);
malloc(m) :开辟m字节长度的地址空间,并返回这段空间的首地址
sizeof(x):计算x的长度
free(p):释放指针p所指变量的存储空间
C++的动态存储分配:
new 类型名T(处置列表):申请用于存放T类型对象的内存空间,并依初值列表赋以初值
int *p1 = new int;
int *p1 = new int (10);
delete 指针p
C++中的参数传递:
实参形参三个一致:类型、个数、顺序
参数传递两种方式:
- 传值:
- 传地址:参数为指针变量
引用类型
数组名
指针:
void swap(float *m,float *n){
float t;
t = *m;
*m = *;
*n = t;
}
main(){
float a,b,*p1,*p2;
p1 = &a;
p2 = &b;
swap(p1,p2);
}
数组名:作参数传递的是数组的首地址,对形参数组所做的任何改变都将反应到实参数组中
引用类型:共用同一块空间
- 传递引用给函数与传递指针效果相同,形参变实参也变
- 引用作形参不产生实参副本,直接对实参操作,数据量大时时间空间效率好
- 指针参数调用时容易产生错误且程序的阅读性较差
typedef struct{
ElemType *elem;
int length;
}SqList;
SqList L;//定义变量L,L是SqList 这种类型的,L是个顺序表
L.elem[0]
线性表L的初始化(参数用引用)
Status InitList(SqList &L){
L.elem = new ElemType[MAXSIZE];//为顺序表分配空间
if(!L.elem) exit (OVERFLOW);
L.length = 0;
return OK;
}
销毁线性表L
void DestroyList(SqList &L){
if(L.elem) delete L.elem;
}
清空
void ClearList(SqList &L){
L.length = 0;//长度置为0
}
长度
int GetLength(SqList L){
return L.length;
}
空
int IsEmpty(SqList L){
if(L.length==0) return 1;
else return 0;
}
顺序表取值
int GetElem(SqList L,int i,ElemType &e){
if(i<1||i>L.length) return ERROR;
e = L.elem[i-1];
return OK;
}
顺序表的查找
int LocateElem(SqList L, ElemType e){
for(i=0;i<L.length;i++)
if(L.elem[i]==e) return i+1;
return 0;
}平均查找长度ASL:Pi*Ci (n+1)/2
插入算法
Status ListInsert(SqList &L,int i,ElemType e){
if(i<1||i>L.length+1) return ERROR;//位置合法
if(L.length==MAXSIZE) return ERROR;
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1] = L.elem[j];
L.elem[i-1] = e;
L.length++;
return OK;
}//平均移动次数n/2,时间复杂度O(n)
顺序表的删除
位置合法、元素保留、移动、表长
Status ListDelete(SqList &L,int i){
if((i<1||(i>L.length))) return ERROR;
for(j=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem[j];
L.length--;
return OK;
}//平均移动次数(n-1)/2,时间复杂度O(n),空间O(1)
顺序表优点:存储密度大、随机存取
缺点:插入删除移动大量元素、浪费存储空间、静态存储个数不能自由扩充
2.5线性表的链式表示和实现
单链表由头指针唯一确定,因此单链表可以用头指针的名字来命名,若头指针名是L,则把链表称为表L
结点:
链表:n个结点由指针链组成一个链表
头指针:指向链表中第一个结点的指针
首元结点:存储第一个数据元素a1的结点
头结点:在链表的首元节点之前附设的一个结点
2.5.1单链表的定义和表示
typedef struct Lnode{//声明结点的类型和指向结点的指针类型
ElemType data;
struct Lnode *next;//结点的指针域
}Lnode,*LinkList;//LinkList为指向结构体Lnode的指针类型
定义链表L: LinkList L;
定义结点指针p: LNode *p;
2.5.2单链表基本操作的实现
单链表的初始化(带头结点)
1.生成新结点作头结点,用头指针L指向头结点
2.将头结点的指针域置空
Status InitList(LinkList &L){
L = new LNode;//L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
return OK;
}
判断链表为空(头结点指针域是否为空)
int ListEmpty(LinkList L){
if(L->next) return 0;
else return 1;
}
单链表销毁(从头指针依次释放所有结点)
Status DestroyList(LinkList &L){
Lnode *p;
while(L){
p=L;
L=L->next;
delete p;
}
return OK;
}
清空链表(头指针头结点仍然在)
Status ClearList(LinkList &L){
Lnode *p,*q;
p=L->next;
while(p){
q = p->next;
delete p;
p = q;
}
L->next = NULL;
return OK;//头结点指针域为空
}
单链表表长(从首元结点依次计数所有结点)
int ListLength(LinkList L){
LinkList p;
p=L->next;
i=0;
while(p){
i++;
p=p->next;
}
return i;
}
p=L; p指向头结点
s=L->next; s指向首元节点
取值---取单链表第i个元素的内容
Status GetElem(LinkList L,int i,ElemType &e){
p=L->next;
j=1;
while(p&&j<i){
p=p->next;
++j;
}
if(!p||j>i) return ERROR;
e=p->data;
return OK;
}
查找
按值查找--数据的地址
Lnode *LocateElem(LinkList L,Elemtype e){
//查找e,找到返回e的地址
p=L->next;
while(p&&p->data!=e)
p=p->next;
return p;
}
序号
int LocateElem(LinkList L,Elemtype e){
//查找e,找到返回e的位置序号
p=L->next;
j=1;
while(p&&p->data!=e){
p=p->next;
j++;
}
if(p) return j;
else return 0;
}
插入
Status ListInsert(LinkList &L,int i,ElemType e){
p=L;
j=0;
while(p&&j<i-1){
p=p->next;
++j;
}
if(!p||j>i-1) return ERROR;
s = new LNode;
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
删除
Status ListDelete(LinkList &L,int i,ElemType &e){
p=L;
j=0;
while(p->next&&j<i-1){
p=p->next;
++j;
}
if(!(p->next)||j>i-1) return ERROR;//删除位置不合理
q = p->next;
p->next = q->next;
e = q->data;
delete q;
return OK;
}
1.查找:时间复杂度O(n)
2.插入、删除:一般时间复杂度O(1),前插或删除时间复杂度O(n)
建立单链表:头插法(前插法)
void CreateList(LinkList &L,int n){
L=new LNode;
L->next = NULL;//带头结点
for(i=n;i>0;i--){
p=new LNode;
//p = (LNode*)malloc(sizeof(LNode));
cin>>p->data;
//scanf(&p->data);
p->next = L->next;
L->next = p;
}
}//时间复杂度:O(n)
尾插法(后插法)
void CreateList(LinkList &L,int n){
L=new LNode;
L->next = NULL;//带头结点
r=L;//尾指针r指向头结点
for(i=0;i<n;i++){
p=new LNode;
cin>>p->data;
p->next = NULL;
r->next = p;
r=p;
}
}//时间复杂度:O(n)
2.5.3循环链表
循环条件:p!=L
p->next !=L
尾指针表示单循环链表:a1的存储位置:R->next->next
an的存储位置:R
时间复杂度:O(1)
带尾指针循环链表的合并(Tb合并在Ta之后)
1.p存表头结点 p = Ta->next;
2.Tb表头连接到Ta表尾 Ta->next = Tb->next->next;
3.释放Tb表头结点 delete Tb->next;
4.修改指针 Tb->next = p;
LinkList Connect(LinkList Ta,LinkList Tb){
p = Ta->next;
Ta->next = Tb->next->next;
delete Tb->next;
Tb->next = p;
return Tb;
}//时间复杂度O(1)
2.5.4双向链表
结构定义
typedef struct DuLNode{
Elemtype data;
struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;
插入删除时间复杂均为:O(n)
双向链表的插入
void ListInsert(DuLinkList &L,int i ,ElemType e){
//第i位置插入e
if(!(p=GetElem(L,i))) return ERROR;
s = new DuLNode;
s->data = e;
s->prior = p->prior;
p->prior->next = s;
s->next = p;
p->prior = s;
return OK;
}
双向链表的删除
void ListDelete_DuL(DuLink &L,int i, ElemType &e){
if(!(p=GetElemP_Dul(L,i))) return ERROR;
e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return OK;
}
2.6顺序表和链表的比较
存储密度:结点数据本身所占用的空间/结点占用的空间总量
2.7线性表的合并
A与B合并成新的集合(无重复)
void union(List &La,List Lb){
La_len = ListLength(La);
Lb_len = ListLength(Lb);
for(i=1;i<Lb_len;i++){
GetElem(Lb,i,e);
if(!LocateElem(La,e)) ListInsert(&La,++La_len,e);
}
}
时间复杂度:O(ListLength(La)*ListLength(Lb))
La、Lb合并成新的Lc
1.创建空表Lc
2.从La或Lb中取较小的放入Lc表最后
3.将一个表剩余结点插入
顺序表实现
void MergeList_Sq(SqList LA,SqList LB,SqList &LC){
pa = LA.elem;
pb = LB.elem;
//指针pa和pb的初值分别指向两个表的第一个元素
LC.length = LA.length + LB.length;//新表长度
LC.elem = new ElemType[LC.length];
pc = LC.elem;//指针pc指向新表的第一个元素
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++;
}
时间复杂度:O(ListLength(La)+ListLength(Lb))
空间复杂度:O(ListLength(La)+ListLength(Lb))
pc->next = pa?pa:pb;
if(pa) pc->next = pa;
else pc->next = pb;
链表实现
void MergeList_L(LinkList &La,LinkList n&Lb,LinkList &Lc){
pa = La->next;
pb = Lb->next;
pc=Lc=La;//用La的头结点作为Lc的头结点
while(pa&&pb){
if(pa->data<=pb->data){
pc->next = pa;
pc = pa;
pa = pa->next;
}
else{
pc->next = pb;
pc = pb;
pb = pb->next;
}
pc->next = pa?pa:pb;
delete Lb;//释放Lb的头结点
}
}
时间复杂度:O(ListLength(La)+ListLength(Lb))
空间复杂度:O(1)
多项式相加
typedef struct PNode{
float coef;
int expn;
struct PNode *next;
}PNode,*Polynomial;
多项式创建
void CreatePolyn(Polynomial &P,int n){
//建立多项式的有序链表P
P = new PNode;
P->next = NULL;//先建立一个带头结点的单链表
for(i=1;i<=n;++i){
s = new PNode;
cin>>s->coef>>s->expn;
pre = P;//pre用于保存q的前驱,初值为头结点
q = P->next;//q初始化,指向首元节点
while(q&&q->expn<s->expn){//找到第一个大于输入项指数的项*q
pre = q;
q = q->next;
}
s->next = q;//将输入项s插入到q和其前驱结点pre之间
pre->next = s;
}
}
struct Book{
char id[20];
char name[50];
int price;
};
typedef struct{//顺序表
Book *elem;
int length;
}SqList;
typedef struct LNode{
Book data;//链表
struct LNode *next;
}LNode,*LinkList;
冒泡:从小到大
C语言:
‘\0’
三个字符串比较大小:
#include <stdio.h>
#include <string.h>
void cmpswa(char *s1,char *s2)
{
char d[15];
strcpy(d,s2);
strcpy(s2,s1);
strcpy(s1,d);
}
int main()
{
char a[15];
char b[15];
char c[15];
printf("请输入三个字符串:");
scanf("%s%s%s",&a,&b,&c);
if(strcmp(a,b)>0) {
cmpswa(a,b);
}
if(strcmp(a,c)>0) {
cmpswa(a,c);
}
if(strcmp(b,c)>0) {
cmpswa(b,c);
}
printf("%s,%s,%s",a,b,c);
return 0;
}
汉诺塔问题:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void move(int x, int y)
{
printf("%c->%c\n", x, y);
}
void hanoi(int n, char a, char b, char c)
{
if (n == 1)
{
move(a, c);
}
else
{
hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
move(a, c);//将A座上最后一个盘子移向C座
hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
}
}
//move中的实参与hanoi函数中的形参相对应,而hanoi函数中形参a,b,c所对应的值也是在有规律的变化
int main()
{
int n = 0;
scanf("%d", &n);
hanoi(n, 'A', 'B', 'C');
return 0;
}
二维数组必须指定列数
形参第一维大小省略,第二维不能省略,且大小与实参数组第二维大小相同
指针:
定义指针变量时,左侧应有类型名,否则就不是定义指针变量
Int *p;
数组元素的指针:就是数组元素的地址
引用数组元素时指针的运算:
指针已以指向一个数组元素时,可以对指针进行以下运算:
p+1表示指向同一数组中的下一个元素,p-1指向上一个,p++,++p,p--,--p
p1-p2(二者都指向同一数组中的元素),结果是两个地址之差除以数组元素的长度
如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,
或者说它们指向数组a中序号为i的元素
*(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]
[]实际是变址运算符,即将a[i]按a+i计算出地址,然后找出此地址单元中的值
p+1,根据定义的基类型加上一个数组元素所占用的字节数
作业详情
21大数据-线性表1
- 单选题(共4题,40分)
1. (单选题)1.顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是( )。
- A. 110
- B. 108
- C. 100
- D. 120
我的答案: B正确答案: B
2. (单选题)22.在线性表的下列运算中,不改变数据元素之间结构关系的运算是()。
- A. 插入
- B. 删除
- C. 排序
- D. 定位
我的答案: D正确答案: D
3. (单选题)顺序表具有随机存取特性指的是( )
- A. 査找值为x的元素与顺序表中元素的个数n无关
- B. 查找值为x的元素与顺序表中元素的个数n有关
- C. 査找序号为i的元素与顺序表中元素的个数n无关
- D. 査找序号为i的元素与顺序表中元素的个数n有关
我的答案: C正确答案: C
4. (单选题)在n个结点的顺序表中,算法的时间复杂度是O(1)的操作是( )。
- A. 访问第i个结点(1≤i≤n)和求第i个结点的直接前驱(2≤i≤n)
- B. 在第i结点后插入一个新结点(1≤i≤n)
- C. 删除第i个结点(1≤i≤n)
- D. 将n个结点从小到大排序
我的答案: A正确答案: A
二. 填空题(共4题,40分)
5. (填空题)在有n个元素的顺序表中删除任意一个元素所需移动元素的平均次数为____________我的答案:(1) (n-1)/2正确答案:(1) (n-1)/2;
6. (填空题)在一个长度为n(n≥1)的顺序表中删除第i个元素(1≤i≤n)时需向前移我的答案:(1) n-i 正确答案:(1) n-i;
7. (填空题)在有n个元素的顺序表中的任意位置插为____________入一个元素所需移动元素的平均次数我的答案:(1) n/2正确答案:(1) n/2;
N n-1 1 0
8. (填空题)6.在长度为n的顺序表中L中将所有值为x的元素替换成y,该算法的时间复杂度为____________
我的答案: (1) O(n) 正确答案:(1) O(n)
三. 判断题(共2题,20分)
9. (判断题)顺序表具有随机存取特性,所以查找值为x的元素的时间复杂度为O(1)。
- A. 对
- B. 错
我的答案: 错正确答案: 错
10. (判断题)从长度为n的顺序表中删除任何一个元素所需要的时间均为O(n)。
- A. 对
- B. 错
我的答案: 错正确答案: 错
21大数据-链表2
一. 单选题(共9题,90分)
1. (单选题)链表不具有的特点是( )
- A. 可随机访问任一元素
- B. 插人、删除不需要移动元素
- C. 不必事先估计存储空间
- D. 所需空间与线性表长度成正比
我的答案: A正确答案: A
2. (单选题)当线性表采用链式存储结构时,各结点之间的地址( )
- A. 必须是连续的
- B. 一定是不连续的
- C. 部分地址必须是连续的
- D. 连续与否均可以
我的答案: D正确答案: D
3. (单选题)
17.[2016年第1题】已知表头元素为c的单链表在内存中的存储状态如图2.1所示。现将f存放于1014H处并插入到单链表中,若f在逻辑上位于a和e之间,则a,e,f的“链接地址”依次是( )
- A. 1010H,1014H,1004H
- B. 1010H,1004H,1014H
- C. 1014H,1010H,1004H
- D. 1014H,1004H,1010H
我的答案: C正确答案: D
4. (单选题)16.[2013年第1题】已知两个长度分别为m和n的升序链表,若将它们合并为一个长度为m+n的降序链表,则最坏情况下的时间复杂度是()。
- A. O(n)
- B. O(mxn)
- C. O(min(m,n))
- D. O(max(m,n))
我的答案: D正确答案: D
5. (单选题)20.将长度为n的单链表链接在长度为m的单链表之后的算法的时间复杂度为()
- A. O(1)
- B. O(n)
- C. O(m)
- D. O(m+n)
我的答案: C正确答案: C
6. (单选题)在不带头结点的循环单链表L中,尾结点p的条件是( )
- A. L!=NULL
- B. L->next!=L
- C. p==NULL
- D. p->next == L
我的答案: D正确答案: D
7. (单选题)2401.在带头结点的循环单链表L中,至少有一个结点的条件是( )
- A. L->next!=NULL
- B. L->next!=L
- C. p==NULL
- D. p->next==L
我的答案: A正确答案: B
8. (单选题)21.在长度为n(n≥1)的双链表L中,删除p所指结点的时间复杂度为( )
- A. O(1)
- B. O(n)
- C. O(n 2)
- D. O(nlog 2n)
我的答案: B正确答案: A
9. (单选题)20.在长度为n(n≥1)的双链表L中,删除尾结点的时间复杂度为( )
- A. O(1)
- B. O(n)
- C. O(n 2)
- D. O(nlog 2n)
我的答案: A正确答案: B