目录
基础定义:
-
线性表:是具有相同特性数据元素的有限序列,所含个数叫做线性表的长度,n为0时,是一个空表。线性表开头的结点称为表头(head)结尾结点称为表尾(tail),元素与它的位置有联系也可以无联系,例如有序线性表(递增顺序排列),无序线性表,除表头和表尾元素之外,其他元素只有一个前驱也只有一个后驱,这是线性表的逻辑特性。
-
线性表的存储结构:顺序存储结构(顺序表)、链式存储结构(链表)
-
顺序表:把线性表中所有元素按照逻辑顺序,依次存储到从指定的存储位置开始的一块连续的储存空间中。
-
链表:链表存储中,每个结点不仅包含所有元素的信息,还包含元素之间的逻辑关系的信息,例如前驱结点包含后继结点的地址信息。链表有五种以下形式:
-
单链表:---,编码中可以采用尾插法,头插法创建
-
双链表:---
-
循环单链表:最后一个结点的指针域指向第一个结点
-
循环双链表:在上述基础上,第一个结点的prev指针指向最后一个结点。
-
静态链表:借助一维数组实现的链表。与一般链表结构区别:一般链表结构节点空间来自整个内存,静态链表则来自于一个结构体数组。
-
-
-
两种存储结构比较:
-
顺序表特性:随机访问特性(只要知道0点位置,就能找到任何一个元素的位置),占用连续的储存空间(预先一次性分配,操作过程中始终不变)
-
链表特性:不支持随机访问,读取某个元素必须遍历,储存空间效率稍低于顺序表,链表存储空间支持动态分配,插入删除元素效率更高。
-
推荐一个数据结构演示网站https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
代码演示:
只演示一些个人认为有必要实践的代码,来实现一下这题吧:
代码内容:单链表结点结构定义,尾插法创建单链表(有头结点),打印单链表,递增顺序合并单链表。
Q:A和B都是两个单链表,其中元素递增,将其合并为仍然有序的链表
#include<iostream>
using namespace std;
//#define MAXSIZE 100
定义顺序表
//typedef struct {
// int data[MAXSIZE];
// int length;
//}SList;
//单链表结点结构体
typedef struct LNode {
int data;
struct LNode* next;
}LNode;
//用来初始化单链表
int A[10] = { 2,6,7,9,11,12,14,24,44,55 };
int B[5] = { 8,16,25,27,30 };
//尾插法建立一个单链表
void initLListR(LNode *&llist,int a[],int n) {
LNode* newNode, *last; //last表示尾结点
llist = (LNode*)malloc(sizeof(LNode));//申请一个头结点空间
llist->next = NULL;
last = llist; //开始时候尾结点和头结点是一个
for (int i=0; i < n; i++) {
newNode = (LNode*)malloc(sizeof(LNode));
newNode->data = a[i];
newNode->next = NULL; //这样写也可以①
last->next = newNode;
last = last->next;
}
//last->next = NULL; 书二中的写法②
}
//打印链表
void printLList(LNode* llist) {
LNode* temp = llist->next; //从头结点下一个打印
while (temp!= NULL)
{
cout << temp->data <<" ";
temp = temp->next;
}
cout << endl;
}
//顺序合并
void merge(LNode* A, LNode* B, LNode*& C) {
LNode* a = A->next;
LNode* b = B->next;
LNode* temp;
C = A; //用A的头结点作为合并后的头结点
C->next = NULL;
//free(B) 可以在所有程序完成后同意释放
temp = C;
//比大小一个一个合并
while (a!=NULL&&b!=NULL)
{
if (a->data < b->data) {
temp->next = a;
a = a->next;
}
else {
temp->next = b;
b = b->next;
}
temp = temp->next;
}
//temp->next = NULL;
//将剩余的链表链接在合并链表后
if (a != NULL) {
temp->next = a;
}
if (b != NULL) {
temp->next = b;
}
}
int main() {
LNode *AL,*BL,*CL;
//尾插法创建两个新链表
initLListR(AL, A, 10);
initLListR(BL, B, 5);
//打印两个单链表
cout << "AL:";
printLList(AL);
cout << "BL:";
printLList(BL);
//顺序合并
merge(AL, BL, CL);
//打印合并后的单链表
cout << "CL:";
printLList(CL);
return 0;
}
运行结果:
后面类似的单链表操作以及双链表能够理解其机制(参考书二)自己编码实现即可!
多说几句:
单链表、双链表的定义,一些操作例如生成链表、插入元素、删除元素、打印元素、查找元素等等都应该是必须掌握其机制,主要要理解其指针到底怎么操作的。
咋们来继续看一个问题——逆置问题
何为逆置问题:这样来理解,设置两个变量i和j,i指向第一个元素,j指向最后一个元素,边交换i和j所指的元素,相向而行,知道相遇,说实话有点想浪漫~
对于一个数组,操作很简单,更难的一些操作参见书二,有具体的代码示范。
for (int i = lef, j = right; i < j; ++i, ++j) {
temp = a[i][j];
a[i] = a[j];
a[j] = temp[i];
}