复习用笔记,参考资料《数据结构》严蔚敏、青岛大学-王卓
c++语法:
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#include <iostream>
cin >> x;//输入
cout << "输出内容" << endl; //输出 endl为换行
动态分配内存
int *p = new int; //在堆空间创建int类型变量,new的结果是int*类型,将结果赋值给p。new运算符返回一个指向所分配的存储空间的第一个单元的指针
//C:int *p =(int*)malloc(sizeof(int))
动态释放内存
delete p,delete[] p;//释放数组必须是delete[];delete p仅仅释放了p数组的首元素,内存空间并没有释放完全
//释放p指向的堆空间的int变量.凡是由new运算符分配的内存空间,一定要在使用完后用delete释放。
//C:free(p)
1、头结点
头结点在首元结点前,指针域指向首元结点(存储第一个数据元素的节点)。若有头结点,头指针指向结点为头结点,若无,则指向首元结点。
2、存储结构
typedef struct LNode {
ElemType data; //结点的数据域
struct LNode *next; //结点的指针域
} LNode, *LinkList; //LinkList为指向结构体LNode的指针类型
3、单链表的初始化(带头结点)
Status InitList(LinkList &L)
{
//构造一个空的单链表L
L = new LNode; //生成新结点作为头结点,用头指针L指向头结点
L->next = NULL; //头结点的指针域置空
return OK;
}
4、判断空表
无头结点时,头指针为空表示空表(L=NULL)
有头结点时,头结点指针域为空表示空表(L->next=NULL)
bool ListEmpty(LinkList L)
{
if(L->next==NULL)return 1;
return 0;
}
//下列算法均为带头结点版本
5、单链表销毁(销毁后链表不存在)
从头指针开始,依次释放所有结点
Status DestroyList(LinkList &L)
{
/* 初始条件:线性表L已存在。操作结果:销毁线性表L */
LinkList p;//LNode *p;
while(L) {
p = L;
L = L->next;
delete p;
}
return OK;
}
6、清空链表(链表存在但无元素,头指针头结点存在)
依次释放所有结点,并将头结点指针域设置为空【两指针同时运行】
Status ClearList(LinkList &L)
{
LNode *p, *q;
p = L->next;
while (p)
{
q = p->next;
delete p;
p=q;
}
L->next = NULL;//头结点指针域为空
return OK;
}
7、表长
遍历链表,统计结点数
int ListLength(LinkList L)
{
int count=0;
LNode *p;
p=L->next;
while(p){
count++;
p=p->next;
}
return count;
}
8、取值
首元结点出发,遍历链表。用e返回第i个元素值。
Status GetElem(LinkList L, int i, ElemType &e)
{
LNode *p;
p=L->next;
int count=1;
while(count<i&&p){
p=p->next;
++count;//遍历至p为空或p指向第i个元素
}
if(!p||count>i)return ERROR;//第i个元素不存在
e=p->data;
return OK;
} //GetElem
当1<=i<=n,频度为i-1;每个元素取值概率为1/n;
ASL=1/n=n-1/2。时间复杂度为O(n)
9、查找
按值查找,返回地址
LNode *LocateElem(LinkList L, int e) //按值查找地址
{
LNode *p;
p=L->next;
while(p&&p->data!=e)
{
p=p->next;
}
return p;//找到返回地址p,查找失败返回NULL
} //LocateElem
按值查找,返回位置序号
int LocateElem(LinkList L, int e) //略有改动 算法2.8 按值查找位置序号
{
LNode *p;
int count=1;
p=L->next;
while(p&&p->data!=e)
{
count++;
p=p->next;
}
if(p)return count;
else return 0;
} //LocateElem
时间复杂度类似取值,为O(n)
10、插入
在第i个结点前插入值为e的新结点
①找到第i-1个元素的存储位置p
②生成一个数据域为e的新结点s
③插入新结点:
1)新结点指针域指向第i个元素结点 s->next=p->next
2)第i-1个元素结点指向新结点 p->next=s
*插入步骤不可互换,否则丢失Ai位置
Status ListInsert(LinkList &L, int i, ElemType e)
{
LNode *p,*s;
int j=0;
p=L;
while(p&&j<i-1){
++j;
p=p->next;
}//让p指向Ai-1位置
if(!p||j>i-1) return ERROR;//非法
s=new LNode;//生成新结点
s->data=e;
s->next=p->next;
p->next=s;//插入
return OK;
} //ListInsert
必须先找到第i-1个结点,时间复杂度与取值类似,仍为O(n)
11、删除
①首先找到Ai-1的存储位置p,保存要删除Ai值
②令p->next指向Ai+1
③释放Ai空间
Status ListDelete(LinkList &L, int i)
{
LNode *p, *q;
int j = 0;
p = L;
while (p->next && j < i - 1)
{
p = p->next;
++j;
} // 让p指向Ai-1位置
if (!(p->next)|| j > i - 1)
return ERROR; // 非法
q=p->next;
p->next=q->next;
delete q;
return OK;
} //ListDelete
时间复杂度与插入算法类似,仍为O(n)
*删除算法中循环条件(p->next&&j<i-1)与插入算法中循环条件(p&&j<i-1)有所区别,插入算法有n+1个位置,删除只有n个,如一样会造成引用空指针使删除失败。
12、输出链表
void ListPrint(LinkList L)
{
LNode *p;
for(p = L->next; p; p = p->next)
cout << p->data << (p->next ? ' ' : '\n');
}
13、单链表创建
1)尾插法(元素生成在链表尾部)-适用于顺序存入
1.从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。
2.初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。
void CreateList_R(LinkList &L, int n) //后插法创建单链表
{
LNode *tail=NULL,*p;
L=new LNode;
L->next=NULL;
tail=L;//尾指针指向头结点
for(int i=0;i<n;++i)
{
p=new LNode;//形成新结点
cin >> p->data;
p->next=NULL;
tail->next=p;//新结点插入尾结点后
tail=p;//tail指向新的尾结点p
}
}
2)头插法(元素插入在链表头部)-适合逆序存入
1、从一个空表开始,重复读入数据;
2、生成新结点,将读入数据存放到新结点的数据域中
3.从最后一个结点开始,依次将各结点插入到链表的前端
void CreateList_H(LinkList &L, int n) // 头插法创建单链表
{
LNode *p;
L = new LNode;
L->next = NULL;//建立链表
for (int i = 0; i < n; ++i)
{
p = new LNode; // 形成新结点
cin >> p->data;
p->next=L->next;//插入表头
L->next=p;
}
}
时间复杂度O(n)