学习23王道数据结构笔记(整体代码在最后)
一、分析
数据结构的三要素:数据的逻辑结构、存储结构、运算。
① 在逻辑结构中,分为线性结构和非线性结构。线性结构中有:一般线性表、栈、队列、串、数组;非线性结构有:集合、一般树、二叉树、有向图、无向图。
② 在存储结构中,主要分为四大存储:顺序存储、链式存储、索引存储、散列存储
③ 运算包括:定义和实现。
定义:针对逻辑结构,指出运算的功能;
实现:针对存储结构,指出运算的具体操作步骤。
二、特点
我对线性表的顺序存储以及链式存储记一下它们的不同点。这里的单链表是就典型的线性表通过链式存储实现的线性表。
顺序表 | 链表 | |
弹性(可扩容) | 不太行 | 可行 |
增删 | 不太行 | 可行 |
查 | 可行 | 不太行 |
1.对扩容分析:
顺序表 链表
① 逻辑上相邻的元素,对应的物理存 ①逻辑上相邻的元素,物理存
储位置也相邻。费空间 储位置不一定相邻。
②需要一开始给足空间,空间过大闲 ②可扩充
置,过小易溢出。不可扩充
2.对增删改查操作分析:
顺序表 链表
有序 无序 有序 无序
按值查找 O(log2n) O(n) O(n) O(n)
按序号查找 O(1) O(1) O(n) O(n)
插入删除 需要移动插入删除后的后面所有元素 只需要修改相关结点的指针域即可
三、代码操作
下面是对单链表的基本操作:(均为带头结点操作)
①定义单链表
//定义单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
②初始化结点
只有当这个结点需要开辟空间存储数据时才会用到,这里用LNode *开辟而不用LinkList就是为了强调这个是一个单纯的结点,后面没跟链表。
//初始化结点
void InitLNode(LNode *&s,ElemType e){
s = (LNode *)malloc(sizeof(LNode)); //强调结点
s->next = NULL;
s->data = e;
}
③头插法建立单链表
一直在头结点L的后面插入结点
转变过程
输入后的链表
//头插法建立单链表(带头结点)
LinkList List_HeadInsert(LinkList &L){
LNode *s;
int x;
L = (LinkList)malloc(sizeof(LNode)); //创建头结点,强调链表
L->next = NULL;
cin >> x;
while(x != -1){ //循环输入这里是关键,先给s开空间,再给指,但后通过L,s的赋值定义为头插还是尾插,一定要熟练
InitLNode(s, x);
s->next = L->next;
L->next = s;
cin >> x;
}
return L; //L指向的是表头
}
④输出链表
顺便加了一个gross记录表长哈,这个可以在定义链表在中加个length赋值给它哈。
//单链表的遍历
void PrintList(LinkList head){
LNode* current = head;
int gross = 0;
cout << "遍历单链表的值:" << endl;
/*while(current != NULL){ //不带头结点时,带头结点用这个输出会将头结点也一并输出
gross ++;
cout << current->data << "->";
current = current->next;
}*/
while(current->next != NULL){
gross++;
current = current->next;
cout << current->data << "->";
}
cout << "\n链表的数量为:\n" << gross << endl << endl;
}
④测试
到这就可以测试了,输出一下代码看看:
int main(){ //编号从1开始
//头插法建立单链表
LinkList L1;
cout << "头插法建立单链表,输入-1结束:" << endl;
List_HeadInsert(L1); //头插法建立单链表
PrintList(L1);return 0;
}
运行截图
⑤尾插法建立单链表
头插法会一直往插入的结点后插入。来张图要好理解的多
//尾插法建立单链表(带头结点)
LinkList List_TailInsert(LinkList &L){
int x;
L = (LinkList)malloc(sizeof(LNode)); //创建头结点,强调链表
LNode *s, *r=L;
cin >> x;
while(x != -1){
InitLNode(s, x);
r->next = s;
r = s; //r指向新的表尾指针
cin >> x;
}
r->next = NULL; //尾结点指针置空
return L;
}
⑥按序号查找结点
//按序号查找结点
LNode *GetElem(LinkList L, int i){ //强调结点
int j = 1; //计数
LNode *p = L->next;
//对i进行逻辑判断,加强代码的健壮性
if(i == 0)
return L;
if(i < 0)
return NULL;
while(p && j<i){ //判断了p就不用再去通过头结点判断链表是否为空了
p = p->next;
j++;
}
return p;
}
⑦按值查找表结点
//按值查找表结点
LNode *LocateElem(LinkList L, ElemType e){
LNode *p = L->next;
int gross = 1; //记录第几个数
while(p && p->data!=e){
p = p->next;
gross++;
}
return p;
}
⑧尾插法插入结点 (在给定结点后插入一个结点)
//尾插法插入结点 (在给定结点后插入一个结点)
bool InsertLNode(LinkList &L, ElemType e, int i){ //(需要插入的链表,插入值,插入的位置)
if(L == NULL)
return false;
else{
LNode *p = GetElem(L , i), *s;
if(p == NULL)
return false;
else{
InitLNode(s , e);
s->next = p->next;
p->next = s;
}
}
}
⑨前插法插入结点(在给定结点前插入一个结点)
//前插法插入结点(在给定结点前插入一个结点)
bool FrontInsertNode(LinkList &L, ElemType e, int i){
if(L == NULL)
return false;
else{
LNode *p = GetElem(L, i), *s;
if(p == NULL)
return false;
else{
InitLNode(s, e);
s->next = p->next;
p->next = s;
temp = p->data; //这里区别与后插
p->data = s->data;
s->data = temp;
}
}
}
⑩删除结点操作
//删除结点操作
bool DelectLNode(LinkList L, int i){
if(L == NULL)
return false;
else{
LNode *p = GetElem(L, i-1),*s; //找该结点的前驱结点
if(p == NULL)
return false;
else{
InitLNode(s, 0);
s = p->next;
p->next = s->next;
free(s);
}
}
}
五、整体代码
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define MaxSize 50
typedef int ElemType;
int n,m;
int arr[MaxSize],brr[MaxSize];
bool flag,judge;
ElemType temp,over; //用于过度
//单链表的应用
//定义单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
//初始化结点
void InitLNode(LNode *&s,ElemType e){
s = (LNode *)malloc(sizeof(LNode)); //强调结点
s->next = NULL;
s->data = e;
}
//头插法建立单链表(带头结点)
LinkList List_HeadInsert(LinkList &L){
LNode *s;
int x;
L = (LinkList)malloc(sizeof(LNode)); //创建头结点,强调链表
L->next = NULL;
cin >> x;
while(x != -1){ //循环输入这里是关键,先给s开空间,再给指,但后通过L,s的赋值定义为头插还是尾插,一定要熟练
InitLNode(s, x);
s->next = L->next;
L->next = s;
cin >> x;
}
return L; //L指向的是表头
}
//尾插法建立单链表(带头结点)
LinkList List_TailInsert(LinkList &L){
int x;
L = (LinkList)malloc(sizeof(LNode)); //创建头结点,强调链表
LNode *s, *r=L;
cin >> x;
while(x != -1){
InitLNode(s, x);
r->next = s;
r = s; //r指向新的表尾指针
cin >> x;
}
r->next = NULL; //尾结点指针置空
return L;
}
//按序号查找结点
LNode *GetElem(LinkList L, int i){ //强调结点
int j = 1; //计数
LNode *p = L->next;
//对i进行逻辑判断,加强代码的健壮性
if(i == 0)
return L;
if(i < 0)
return NULL;
while(p && j<i){ //判断了p就不用再去通过头结点判断链表是否为空了
p = p->next;
j++;
}
return p;
}
//按值查找表结点
LNode *LocateElem(LinkList L, ElemType e){
LNode *p = L->next;
int gross = 1; //记录第几个数
while(p && p->data!=e){
p = p->next;
gross++;
}
return p;
}
//单链表的遍历
void PrintList(LinkList head){
LNode* current = head;
int gross = 0;
cout << "遍历单链表的值:" << endl;
/*while(current != NULL){ //不带头结点时,带头结点用这个输出会将头结点也一并输出
gross ++;
cout << current->data << "->";
current = current->next;
}*/
while(current->next != NULL){
gross++;
current = current->next;
cout << current->data << "->";
}
cout << "\n链表的数量为:\n" << gross << endl << endl;
}
//尾插法插入结点 (在给定结点后插入一个结点)
bool InsertLNode(LinkList &L, ElemType e, int i){ //(需要插入的链表,插入值,插入的位置)
if(L == NULL)
return false;
else{
LNode *p = GetElem(L , i), *s;
if(p == NULL)
return false;
else{
InitLNode(s , e);
s->next = p->next;
p->next = s;
}
}
}
//前插法插入结点(在给定结点前插入一个结点)
bool FrontInsertNode(LinkList &L, ElemType e, int i){
if(L == NULL)
return false;
else{
LNode *p = GetElem(L, i), *s;
if(p == NULL)
return false;
else{
InitLNode(s, e);
s->next = p->next;
p->next = s;
temp = p->data; //这里区别与后插
p->data = s->data;
s->data = temp;
}
}
}
//删除结点操作
bool DelectLNode(LinkList L, int i){
if(L == NULL)
return false;
else{
LNode *p = GetElem(L, i-1),*s; //找该结点的前驱结点
if(p == NULL)
return false;
else{
InitLNode(s, 0);
s = p->next;
p->next = s->next;
free(s);
}
}
}
int main(){ //编号从1开始
//头插法建立单链表
LinkList L1;
cout << "头插法建立单链表,输入-1结束:" << endl;
List_HeadInsert(L1); //头插法建立单链表
PrintList(L1);
//尾插法建立单链表
LinkList L2;
cout << "尾插法建立单链表,输入-1结束:" << endl;
List_TailInsert(L2); //尾插法建立单链表
PrintList(L2);
//按序号查找结点
LinkList L3;
cout << "按序号查找结点,序号为 3 的结点值为:" << endl;
List_HeadInsert(L3); //头插法建表
LNode *p1 = GetElem(L3 , 3); //序号查结点
if(p1 == NULL)
cout << "查无此值" << endl << endl;
else
cout << p1->data << endl << endl;
//按值查找表结点
LinkList L4;
cout << "按值查找表结点,值为 35 的结点为:" << endl;
List_TailInsert(L4); //尾插法建表
LNode *p2 = LocateElem(L4 , 35); //按值查结点
if(p2 == NULL)
cout << "查无此值" << endl << endl;
else
cout << p2->data << endl << endl;
//在给定结点位置后插入一个结点
LinkList L5;
cout << "在序号为5的结点后插入值 555 :" << endl;
List_HeadInsert(L5); //头插法建表
InsertLNode(L5, 555, 5); //插入一个结点
PrintList(L5);
//在给定结点位置前插入一个结点
LinkList L6;
cout << "在序号为6的结点前插入 666 :" << endl;
List_TailInsert(L6);
FrontInsertNode(L6, 666, 6);
PrintList(L6);
//删除给定结点位置的结点
LinkList L7;
cout << "删除给定结点位置的结点: " <<endl;
List_HeadInsert(L7);
DelectLNode(L7, 7);
PrintList(L7);
return 0;
}
整体运行截图: