目录
一、顺序表
用一组地址连续的存储单元依次存储线性表的数据元素的方式,具有顺序存储结构的特点。
线性表的顺序存储结构是一种随机存取的存储结构
注意:顺序表的 a(i) 对应数组下标的 i-1,顺序表中位置要比数组中多1
1、顺序表的存储结构
#define MAXSIZE 100 //顺序表可能达到的最大长度
#define LISTINCREMENT 2 //线性表存储空间的分配增量
typedef struct
{
ElemType *elem; //存储空间的基地址
int length; //当前长度
int listsize; //当前分配的存储容量
}SqList; //顺序表的结构类型为SqList
2、创建一个空顺序表
Status InitList(SqList &L)
{
L.elem = (ElemType *)malloc(MAXSIZE*sizeof(ElemType));
//L.elem = new ElemType[MAXSIZE];
if (!L.elem) exit(OVERFLOW); //存储分配失败退出
L.length = 0; //空表长度为0
L.listsize = MAXSIZE; //顺序表的当前存储量为最大
return OK;
}
3、顺序表取值
用e返回第 i 个数据元素的值
Status GetElem(SqList L, int i, ElemType &e)
{
if(L.length==0 || i < 1 || i > L.length) return ERROR;
e = L.elem[i - 1]; //a(i)对应的数组是i-1
return OK;
}
4、顺序表查找对应的值
在顺序表中找到值为e的数据元素,并返回其在顺序表中的位置
int FindELem(SqList L, ElemType e)
{
for (i = 0; i < L.length; i++)
if(L.elem[i] == e)
return i + 1; //顺序表中的位置要比数组多1
return 0;
}
5、顺序表插入元素
在第i个位置插入元素e
数组法
Status Insert(SqList &L, int i, ElemType e)
{
//因为可以插在最后,所以i值的合法范围是 1~L.length+1
if ((i < 1) || (i > L.length + 1))
return ERROR;
if (L.length >= L.listsize) //存储空间已满
{
ElemType *newbase;
newbase = (ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase) return;
L.elem = newbase;
L.listsize += LISTINCREMENT;
}
for (j = L.length-1; j >= i-1; j--) //j=L.length-1表示从顺序表最后一个元素开始
L.elem[j + 1] = L.elem[j]; //插入位置及之后的元素后移
L.elem[i - 1] = e;
L.length ++;
return OK;
}
指针法
void InsertList_Sq(SqList &L, int i, ElemType e)
{
if(i<1||i>L.length+1) return;
if(L.length>=L.listsize)
{
ElemType *newbase;
newbase = (ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase) return;
L.elem = newbase;
L.listsize += LISTINCREMENT;
}
ElemType *p = &(L.elem[i-1]); //指针p指向第i个元素
ElemType *q = &(L.elem[L.length-1]); //指针q指向最后一个元素
for(q ;q>=p;q--)
*(q+1) = *q;
*p = e;
L.length++;
}
6、顺序表删除元素
删除第i个位置的元素,并把要删除的元素放在e中
数组法
Status Delete(SqList &L, int i , ElemType &e)
{
//i值的合法范围是 1~L.length
if ((i < 1) || (i > L.length))
return ERROR;
e = L.elem [i-1];
for (int j = i; j <= L.length - 1; j++) //因为删除了一个元素,所以表长-1
L.elem[j - 1]=L.elem[j]; //被删除元素之后的元素前移
L.length --;
return OK;
}
指针法
void DeleteList_Sq(SqList &L, int i, ElemType &e)
{
if(i<1||i>L.length) return;
ElemType *p = &(L.elem[i-1]);
e = *p;
ElemType *q = L.elem+L.length-1;//把q指针定位到最后一个元素
for(++p;p<=q;++p)
*(p-1) = *p;
L.length--;
}
总结:只有顺序表的取值是O(1),其他操作都是O(n)
二、单链表
- 首元结点:链表中存储第一个数据元素的结点。
- 头结点:是在首元素结点之前附设的一个结点,其指针指向首元结点。
- 头指针:是指向链表中第一个结点的指针。
- 若链表设有头结点,则头指针所指结点为线性表的头结点。
- 若链表不设头结点,则头指针所指结点为该线性表的首元结点。
- 带头结点的单链表为空时:head->next == NULL
- 不带头结点的单链表为空时:head == NULL
1、单链表的存储结构
typedef struct LNode
{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList; //LinkList为指向结构体LNode的指针类型
LNode规定为结点的类型,*LinkList规定为链表类型
eg:LNode *p // LinkList p
2、创建一个空单链表
void InitList( LinkList &L )
{
L = (LinkList)malloc(sizeof(LNode));
if(!L) return;
L->next = NULL;
}
3、单链表取值
取第i个元素的值,用e返回L中第i个数据元素的值
Status GetElem(LinkList L, int i, ElemType &e)
{
LinkList p;
int j = 1;
p = L->next; //p指向首元结点
while (p && j < i) //若j=i则跳出循环 此时p指到i位置
{
p = p->next;
j++;
}
if (!p || j > i) return ERROR; //若p指向空则返回错误
e = p->data;
return OK;
}
4、 单链表查找对应的值
在单链表中找到值为e的数据元素,并返回值为e的结点地址p
LNode *FindELem(LinkList L, Elemtype e)
{
p = L->next;
while (p && p->data != e)
p = p->next;
return p; //查找成功返回值为e的结点地址p, 查找失败p为NULL
}
5、单链表的插入
在第i个位置插入值为e的新结点
Status Listinsert(LinkList &L, int i, ElemType e)
{
LinkList p,s;
int j = 1;
p = L; //p先和头指针指向一个地方
while (p && j < i)
{
p = p->next;
j++; //查找到第i-1个结点,p指向该结点
}
if (!p || j > i ) return ERROR;
s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
6、单链表的删除
删除第i个位置的结点
Status Delete(LinkList &L, int i)
{
LinkList p;
int j = 1;
p=L;
while ((p->next) && (j < i)) //如果第i个结点不为空
{
p = p->next;
j++; //p指向第i-1个结点
}
if (!(p->next) || (j > i)) return ERROR;
q = p->next;
p->next = q->next;
free(q);
return OK;
}
7、头插法创建单链表
输入顺序和输出顺序相反
void HeadCreate( LinkList &L, int n)
{
LinkList p;
L=( LNode* )malloc(sizeof(LNode));
L->next=NULL;
for(int i=0;i < n;i++)
{
p=( LNode* )malloc(sizeof(LNode));
scanf("%d",&p->data);
p->next = L->next;
L->next = p;
}
}
8、尾插法创建单链表
输入顺序和输出顺序相同
void TailCreate( LinkList &L, int n)
{
LinkList p,r;
L=( LNode* )malloc(sizeof(LNode));
L->next=NULL;
r=L;
for(int i=0;i < n;i++)
{
p=( LNode* )malloc(sizeof(LNode));
scanf("%d",&p->data);
r->next = p;
r = p;
}
r->next = NULL;
}
9、单链表的整表删除
Status ClearList( LinkList &L )
{
LinkList p,q;
p=L->next;
while(p) //当p指向的结点不为空
{
q=p->next;
free(p);
p=q;
}
L->next = NULL;
}
三、循环链表
1、将两带头结点的循环链表合并
Status UnionList_H( LinkList LA, LinkList LB )
{
LinkList p,q;
p=LA;
while( p->next != LA ) p=p->next; //把p指针移到LA表尾
q=LB;
while( q->next != LB ) q=q->next; //把q指针移到LB表尾
p->next = LB->next;
free(LB);
q->next = LA;
return LA;
}
2、将两带尾指针的循环链表合并
Status UnionList_T( LinkList RA, LinkList RB )
{
LinkList p;
p=RA->next; //p指针指向表A的头结点
RA->next = RB->next->next; //RA连接表B的首元结点
free(RB->next); //释放表B头结点
RB->next = p;
return RB;
}
3、实例——约瑟夫环问题
N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。 请按退出顺序输出每个退出人的原序号。
输入格式:
输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。
输出格式:
按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。
输入样例:
在这里给出一组输入。例如:
7 3
输出样例:
3 6 2 7 5 1 4
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}Node;
Node *create(int n)
{
Node *p,*L,*s;
int i = 1;
L=(Node*)malloc(sizeof(Node));
p = L;
while(i <= n)
{
s=(Node*)malloc(sizeof(Node));
s->data=i++;
p->next=s;
p=s;
}
s->next = L->next;
free(L);
return s->next; //返回第一个结点的地址
}
int main()
{
int m,n,i;
scanf("%d %d",&n,&m); //n是总人数,数到m退出
Node *p=create(n); //p指针指向循环链表的第一个结点,即编号1的人
Node *temp;
//m%=n;
while(p != p->next)
{
for(i=1;i<m-1;i++) //把p指向要淘汰的前一个人头上
p=p->next;
printf("%d ",p->next->data);
//删除死掉的人
temp=p->next;
p->next=temp->next;
free(temp);
p = p->next;
}
printf("%d",p->data);
}
四、双向链表
1、双向链表存储结构
typedef struct LNode
{
ElemType data;
struct LNode *piror,*next;
}LNode,* DoubleList;
2、双向链表的插入
在第i个位置前插入元素e
Status Insert( DoubleList &L,int i,ElemType e)
{
DoubleList p,s;
int j = 1;
while(!p && j<=i) //让p指针指向第i个结点
{
p=p->next;
j++;
}
s=(LNode*)malloc(sizeof(LNode));
if(s)
{
s->data = e;
s->prior = p->prior; //1
p->prior->next = s; //2
s->next = p; //3
p->prior = s; //4
return OK; //1、2||3、4内部可换,但两组位置不可颠倒
}
return NO;
}
3、双向链表的删除
删除第i个元素,并把删除值存入e中
Status Delete( DoubleList &L,int i,ElemType &e)
{
DoubleList p;
int j = 1;
while(!p && j<=i )
{
p = p->next;
j++;
}
e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
}