一、线性表 Linear list
- 定义
n(n>=0)个具有 相同数据类型 数据元素的 有限序列,其中n为表长.
若用L命名线性表则表示为 L=(a1,a2,a3,..an) - 注意事项
1.相同数据类型说明了,每个元素所占空间是一样的.
2.具有次序.
3.a1叫表头元素,an叫表尾元素.
4.a1没有前驱元素,an没有后继元素.
5.位序1,对数组索引0.
二、研究方式
- 研究逻辑结构
略. - 研究线性表基本运算
初始化、销毁、插入、删除、查找、求表长、判空、输出. - 研究线性表物理结构
根据存物理结构可以将线性表分为:
①顺序表
②链式表
三、顺序表
- 定义
用顺序存储(静态数组)的方式实现线性结构,
逻辑上相邻的元素存储在物理位置上也相邻,
设线性表第一个元素的存放位置是LOC(L),则第二个元素位置为LOC(L)+ sizeof(元素类型),说明对顺序表来说,查找指定位置元素是小菜一碟. - 实现
1.静态分配实现代码如下:
#include
#define MAX_SIZE 6
/**
* ArrayList 定义一个结构体,并使用typedef关键字起别名.
* data 存储数据的数组.
* length 顺序表的实际长度.
*/
typedef struct ArrayList {
int data[MAX_SIZE];
int length;
}List;
/**
* 初始化顺序表.
*/
void initArrayList();
/**
* 顺序表顺序新增元素.
* @param listPointer 指向目标顺序表的指针.
* @return true 插入成功,false失败.
*/
bool insertEle(List* listPointer);
/**
* 指定位置顺序表的新增.
* @param positon 位置.
* @param element 元素.
* @param targetList 指向目标list的指针.
* @return true 插入成功,false失败.
*/
bool insertEleByPosition(int position,int element,List* targetList);
/**
* 指定位置顺序表的删除.
* @param positon 位置.
* @param targetList 指向目标list的指针.
* @return true 删除成功,false失败.
*/
bool deleteEleByPosition(int position,List* targetList);
/**
* 值查找.
* @param value 值.
* @param targetList 指向目标list的指针.
* @return 目标元素位序,-1表示未找到.
*/
int findEleByValue(int value,List* targetList);
/**
* 位置查找.
* @param positon 位置.
* @param targetList 指向目标list的指针.
* @return 目标元素.
*/
int findEleByPosition(int position,List* targetList);
/**
* 展示顺序表元素.
* @param targetList 指向目标顺序表的指针.
*/
void showArrayList(List* listPointer);
// 全局唯一List.
List list;
int main() {
initArrayList();
insertEle(&list);
insertEleByPosition(2,6,&list);
deleteEleByPosition(5,&list);
findEleByValue(3,&list);
findEleByPosition(1,&list);
}
void initArrayList(){
for(int i = 0; i < MAX_SIZE; i++){
list.data[i] = 0;
}
// 初始化元素个数.
list.length = 0;
puts("array list init over.");
}
bool insertEle(List* listPointer) {
puts("please input your value, input \"666\" to exit:");
int element;
while(true) {
scanf("%d",&element);
if(666 == element) {
puts("show arry list elements:");
showArrayList(listPointer);
return true;
}
if(listPointer->length == MAX_SIZE) {
puts("It's beyond its maximum capacity.");
showArrayList(listPointer);
return false;
}
listPointer->data[listPointer->length++] = element;
puts("insert success continue:");
}
}
bool insertEleByPosition(int position,int element,List* targetList) {
if(position > MAX_SIZE || position < 1) {
puts("illegal positon.");
return false;
}
// 1 2 3 4 5 x
if(position > targetList->length){
targetList->data[targetList->length++] = element;
showArrayList(targetList);
return true;
}
for(int i = targetList->length;i>=position;i--) {
targetList->data[i] = targetList->data[i-1];
}
targetList->data[position-1] = element;
targetList->length++;
showArrayList(targetList);
return true;
}
bool deleteEleByPosition(int position,List* targetList){
if(position < 1 || position > targetList->length){
puts("illegal positon.");
return false;
}
if(position == MAX_SIZE){
targetList->data[position-1] = 0;
targetList->length--;
showArrayList(targetList);
return true;
}
for(int i = position;i<=MAX_SIZE;i++) {
targetList->data[i-1] = targetList->data[i];
}
targetList->length--;
showArrayList(targetList);
return true;
}
int findEleByValue(int value,List* targetList){
for(int i=0;ilength;i++){
if(targetList->data[i] == value){
printf("element = %d,positon = %d\n",value,i+1);
return i;
}
}
return -1;
}
int findEleByPosition(int position,List* targetList){
if(position < 1 || position > targetList->length){
puts("illegal positon.");
return false;
}
printf("element = %d,positon = %d\n",targetList->data[position -1],position);
return targetList->data[position -1];
}
void showArrayList(List* listPointer) {
for (int i = 0;ilength;i++){
printf("%d ",listPointer->data[i]);
}
printf("\n");
}
2.动态分配实现
思路
设置了一个容量capacity, 扩容因子FACTOR
当顺序表最大长度达到capacity进行扩容操作,扩容逻辑是按照扩容因子倍数进行扩容.代码:
#include
#include
//初始化默认数组大小.
#define DEFAULT_SIZE 2
//扩容因子为2.
#define FACTOR 2
/**
* ArrayList 定义一个结构体,并使用typedef关键字起别名.
* data 指向一维数组首元素的指针.
* length 顺序表的实际长度.
* capacity 最大容量.
*/
typedef struct ArrayList {
int* data;
int length;
int capacity;
}List;
/**
* 初始化顺序表.
* @return 返回初始化好的顺序表指针.
*/
void initArrayList(List* targetList);
/**
* 顺序表顺序新增元素.
* @param listPointer 指向目标顺序表的指针.
* @return true 插入成功,false失败.
*/
bool insertEle(List* listPointer);
/**
* 指定位置顺序表的新增.
* @param positon 位置.
* @param element 元素.
* @param targetList 指向目标list的指针.
* @return true 插入成功,false失败.
*/
bool insertEleByPosition(int position,int element,List* targetList);
/**
* 扩容方法.
* @param listPointer 指向目标list的指针.
*/
void expandCapacity(List* listPointer);
/**
* 展示顺序表元素.
* @param targetList 指向目标顺序表的指针.
*/
void showArrayList(List* listPointer);
//全局唯一list.
List list;
int main() {
initArrayList(&list);
insertEle(&list);
insertEleByPosition(1,99,&list);
insertEleByPosition(1,999,&list);
}
void initArrayList(List* targetList){
//块作用域的静态变量(块作用域,无链接,静态存储期)
static int array[DEFAULT_SIZE];
for(int i = 0; i < DEFAULT_SIZE; i++){
array[i] = 0;
}
targetList->data = array;
// 初始化元素个数.
targetList->length = 0;
// 初始化最大容量.
targetList->capacity = DEFAULT_SIZE;
puts("array list init over.");
}
bool insertEle(List* listPointer) {
puts("please input your value, input \"666\" to exit:");
int element;
while(true) {
scanf("%d",&element);
if(666 == element) {
puts("show arry list elements:");
showArrayList(listPointer);
return true;
}
//新增元素之前先进行扩容操作.
expandCapacity(listPointer);
listPointer->data[listPointer->length++] = element;
showArrayList(listPointer);
puts("insert success continue:");
}
}
bool insertEleByPosition(int position,int element,List* targetList){
//扩容.
expandCapacity(targetList);
if(position > targetList->capacity || position < 1) {
puts("illegal positon.");
return false;
}
// 1 2 3 4 5 x
if(position > targetList->length){
targetList->data[targetList->length++] = element;
showArrayList(targetList);
return true;
}
for(int i = targetList->length;i>=position;i--) {
targetList->data[i] = targetList->data[i-1];
}
targetList->data[position-1] = element;
targetList->length++;
showArrayList(targetList);
return true;
}
void expandCapacity(List* listPointer){
// 判断是否要扩容.
if(listPointer->length >= listPointer->capacity) {
int* newArrayPtr = (int*)malloc(sizeof(int)*(listPointer->length)*FACTOR);
for(int i=0;i < listPointer->length;i++){
newArrayPtr[i] = listPointer->data[i];
}
//第一次扩容无需释放内存,因为此时data指向的是静态变量,系统会在程序执行完毕自动释放.
if(listPointer->length != DEFAULT_SIZE){
free(listPointer->data);
}
listPointer->data = newArrayPtr;
listPointer->capacity = listPointer->length*FACTOR;
}
}
void showArrayList(List* listPointer) {
for (int i = 0;ilength;i++){
printf("%d ",listPointer->data[i]);
}
printf("\n");
}
3.复杂度分析
四、链表
- 定义:用链式存储(存储结构)实现了线性结构(逻辑结构),一个节点存储一个数据元素.
- 分类
单链表
、双链表
、循环链表
、静态链表
- 单链表2种实现
1.单链表有两种实现:带头结点
,不带头结点
.
带头结点的单链表实现
#include
#include
/**
* 链表的一个节点.
* data 数据域.
* next 指向下一个节点的指针域.
*/
typedef struct Node {
int data;
struct Node * next;
}Node,HeadNode,LinkedList;
/**
* 初始化链表.
* @return 返回链表头结点.
*/
HeadNode* initLinkList();
/**
* 尾插法.
* @param data 数据.
* @param list 指向俩表的指针.
* @return true成功,false失败.
*/
bool tailInsertNode(int data,LinkedList* list);
/**
* 头插法.
* @param data 数据.
* @param list 指向俩表的指针.
* @return true成功,false失败.
*/
bool headInsertNode(int data,LinkedList* list);
/**
* 指定位置插入节点.
* @param data 数据域.
* @param position 位序.
* @param list 链表.
* @return true成功,false失败.
*/
bool positonInsertNode(int data,int positon,LinkedList* list);
/**
* 指定节点之前插入节点.
* @param node 指定节点.
* @param list 链表.
* @return true成功,false失败.
*/
bool nodeInsertNode(int data,Node* targetNode,LinkedList* list);
/**
* 指定位置删除节点.
* @param position 位序.
* @param list 链表.
* @return true成功,false失败.
*/
bool positonDeleteNode(int positon,LinkedList* list);
/**
* 指定节点删除节点.
* @param node 节点.
* @param list 链表.
* @return true成功,false失败.
*/
bool nodeDeleteNode(Node* node,LinkedList* list);
/**
* 展示链表节点.
* @param list 指向链表的指针.
*/
void showLinkedList(LinkedList* list);
/**
* 获取一个链表节点.
* @param data 节点数据域.
* @return 节点.
*/
Node* getNode(int data);
/**
* 获取一个链表长度.
* @param list 链表指针.
* @return 链表长度.
*/
int getLength(LinkedList* list);
/**
* 获取链表指定位置节点.
* @param position 位置.
* @param list 链表指针.
* @return 节点.
*/
Node* getNodeByPosition(int position,LinkedList* list);
int main(){
LinkedList* list = initLinkList();
tailInsertNode(1,list);
tailInsertNode(2,list);
tailInsertNode(3,list);
tailInsertNode(4,list);
headInsertNode(5,list);
positonInsertNode(6,6,list);
positonDeleteNode(1,list);
Node* node = getNodeByPosition(2,list);
nodeInsertNode(7,node,list);
Node* deleteNode = getNodeByPosition(6,list);
nodeDeleteNode(deleteNode,list);
}
LinkedList* initLinkList(){
HeadNode* headNode = (Node*)malloc(sizeof(Node));
headNode->data = 0;
headNode->next = NULL;
puts("HeadNode finished init");
return headNode;
}
bool tailInsertNode(int data,LinkedList* list){
LinkedList* pointer = list;
while(pointer->next != NULL){
pointer = pointer->next;
}
Node* node = getNode(data);
pointer->next = node;
showLinkedList(list);
return true;
}
bool headInsertNode(int data,LinkedList* list){
LinkedList* headerPointer = list;
Node* node = getNode(data);
node->next = headerPointer->next;
headerPointer->next = node;
showLinkedList(list);
return true;
}
bool positonInsertNode(int data, int positon,LinkedList* list){
if(positon < 1) {
puts("illegal position");
return false;
}
int length = getLength(list);
if(positon > length) {
tailInsertNode(data,list);
return true;
}
if(positon == 1){
headInsertNode(data,list);
return true;
}
if(positon <= length) {
Node* node = getNode(data);
LinkedList* pointer = list->next;
for(int i = 0; i < positon-2;i++){
pointer = pointer->next;
}
node->next = pointer->next;
pointer->next = node;
}
showLinkedList(list);
return true;
}
bool positonDeleteNode(int positon,LinkedList* list){
int length = getLength(list);
if(positon < 1 || positon > length) {
puts("illegal position");
return false;
}
if(positon == 1) {
Node* freeNode = list->next;
list->next = (list->next)->next;
free(freeNode);
showLinkedList(list);
return true;
}
LinkedList* pointer = list->next;
for(int i = 0; i < positon-2;i++){
pointer = pointer->next;
}
Node* freeNode = pointer->next;
pointer->next = (pointer->next)->next;
free(freeNode);
showLinkedList(list);
return true;
}
bool nodeInsertNode(int data,Node* targetNode,LinkedList* list){
Node* node = getNode(targetNode->data);
node->next = targetNode->next;
targetNode->next = node;
targetNode->data = data;
showLinkedList(list);
return true;
}
bool nodeDeleteNode(Node* node,LinkedList* list){
// 最后一个元素的删除还得是从头遍历.
if(node->next == NULL){
int length = getLength(list);
positonDeleteNode(length,list);
return true;
}
node->data = (node->next)->data;
Node* freeNode = node->next;
node->next = (node->next)->next;
free(freeNode);
return true;
}
void showLinkedList(LinkedList* list){
// 参数校验.
if(list == NULL){
puts("list can not be null");
return;
}
LinkedList* pointer = list->next;
while(pointer != NULL) {
printf("%d ",pointer->data);
pointer = pointer->next;
}
printf("\n");
}
Node* getNode(int data){
Node* node = (Node*)malloc(sizeof(Node));
node->data = data;
node->next = NULL;
}
Node* getNodeByPosition(int position,LinkedList* list){
int length = getLength(list);
if(position < 1 || position > length){
puts("illegal position");
return NULL;
}
LinkedList* pointer = list;
for(int i = 0;inext;
}
return pointer;
}
int getLength(LinkedList* list){
int length = 0;
Node* pointer = list->next;
while(pointer != NULL) {
length ++;
pointer = pointer->next;
}
return length;
}
不带头结点的单链表
#include
#include
/**
* 链表的一个节点.
* data 数据域.
* next 指向下一个节点的指针域.
*/
typedef struct Node {
int data;
struct Node * next;
}Node,HeadNode,LinkedList;
/**
* 初始化链表.
* @return 返回链表头结点.
*/
HeadNode* initLinkList();
/**
* 尾插法.
* @param data 数据.
* @param list 指向链表指针的引用.
* @return true成功,false失败.
*/
bool tailInsertNode(int data,LinkedList* &list);
/**
* 头插法.
* @param data 数据.
* @param list 指向链表指针的引用.
* @return true成功,false失败.
*/
bool headInsertNode(int data,LinkedList* &list);
/**
* 指定位置插入节点.
* @param data 数据域.
* @param position 位序.
* @param list 链表.
* @return true成功,false失败.
*/
bool positonInsertNode(int data,int positon,LinkedList* &list);
/**
* 指定节点之前插入节点.
* @param node 指定节点.
* @param list 链表.
* @return true成功,false失败.
*/
bool nodeInsertNode(int data,Node* targetNode,LinkedList* list);
/**
* 指定位置删除节点.
* @param position 位序.
* @param list 链表.
* @return true成功,false失败.
*/
bool positonDeleteNode(int positon,LinkedList* &list);
/**
* 指定节点删除节点.
* @param node 节点.
* @param list 链表.
* @return true成功,false失败.
*/
bool nodeDeleteNode(Node* node,LinkedList* list);
/**
* 展示链表节点.
* @param list 指向链表的指针.
*/
void showLinkedList(LinkedList* list);
/**
* 获取一个链表节点.
* @param data 节点数据域.
* @return 节点.
*/
Node* getNode(int data);
/**
* 获取一个链表长度.
* @param list 链表指针.
* @return 链表长度.
*/
int getLength(LinkedList* list);
/**
* 获取链表指定位置节点.
* @param position 位置.
* @param list 链表指针.
* @return 节点.
*/
Node* getNodeByPosition(int position,LinkedList* list);
int main(){
LinkedList* list = NULL;
//插入操作存在初始化首节点的操作所以需要指针引用参数.
tailInsertNode(1,list);
tailInsertNode(2,list);
tailInsertNode(3,list);
tailInsertNode(4,list);
headInsertNode(5,list);
headInsertNode(0,list);
positonInsertNode(6,6,list);
positonDeleteNode(1,list);
Node* node = getNodeByPosition(2,list);
nodeInsertNode(7,node,list);
Node* deleteNode = getNodeByPosition(6,list);
nodeDeleteNode(deleteNode,list);
}
LinkedList* initLinkList(){
HeadNode* headNode = (Node*)malloc(sizeof(Node));
headNode->data = 0;
headNode->next = NULL;
puts("HeadNode finished init");
return headNode;
}
//这里也可以用二维指针,还有更好的方式吗?
bool tailInsertNode(int data,LinkedList* &list){
if(list == NULL) {
list = getNode(data);
showLinkedList(list);
return true;
}
LinkedList* pointer = list;
while(pointer->next != NULL){
pointer = pointer->next;
}
Node* node = getNode(data);
pointer->next = node;
showLinkedList(list);
return true;
}
bool headInsertNode(int data,LinkedList* &list){
if(list == NULL) {
list = getNode(data);
showLinkedList(list);
return true;
}
Node* node = getNode(data);
node->next = list;
list = node;
showLinkedList(list);
return true;
}
bool positonInsertNode(int data, int positon,LinkedList* &list){
if(list == NULL) {
list = getNode(data);
showLinkedList(list);
return true;
}
if(positon < 1) {
puts("illegal position");
return false;
}
int length = getLength(list);
if(positon > length) {
tailInsertNode(data,list);
return true;
}
if(positon == 1){
headInsertNode(data,list);
return true;
}
if(positon <= length) {
Node* node = getNode(data);
LinkedList* pointer = list;
for(int i = 0; i < positon-2;i++){
pointer = pointer->next;
}
int flag = pointer->data;
node->next = pointer->next;
pointer->next = node;
}
showLinkedList(list);
return true;
}
bool positonDeleteNode(int positon,LinkedList* &list){
int length = getLength(list);
if(positon < 1 || positon > length) {
puts("illegal position");
return false;
}
if(positon == 1) {
Node* pointer = list;
Node* freeNode = list;
pointer = list->next;
list->next = NULL;
free(freeNode);
list = pointer;
showLinkedList(list);
return true;
}
LinkedList* pointer = list;
for(int i = 0; i < positon-2;i++){
pointer = pointer->next;
}
Node* freeNode = pointer->next;
pointer->next = (pointer->next)->next;
free(freeNode);
showLinkedList(list);
return true;
}
bool nodeInsertNode(int data,Node* targetNode,LinkedList* list){
Node* node = getNode(targetNode->data);
node->next = targetNode->next;
targetNode->next = node;
targetNode->data = data;
showLinkedList(list);
return true;
}
bool nodeDeleteNode(Node* node,LinkedList* list){
// 最后一个元素的删除还得是从头遍历.
if(node->next == NULL){
int length = getLength(list);
positonDeleteNode(length,list);
return true;
}
node->data = (node->next)->data;
Node* freeNode = node->next;
node->next = (node->next)->next;
free(freeNode);
showLinkedList(list);
return true;
}
void showLinkedList(LinkedList* list){
// 参数校验.
if(list == NULL){
puts("list can not be null");
return;
}
LinkedList* pointer = list;
while(pointer != NULL) {
printf("%d ",pointer->data);
pointer = pointer->next;
}
printf("\n");
}
Node* getNode(int data){
Node* node = (Node*)malloc(sizeof(Node));
node->data = data;
node->next = NULL;
}
Node* getNodeByPosition(int position,LinkedList* list){
int length = getLength(list);
if(position < 1 || position > length){
puts("illegal position");
return NULL;
}
LinkedList* pointer = list;
for(int i = 0;inext;
}
return pointer;
}
int getLength(LinkedList* list){
int length = 0;
Node* pointer = list;
while(pointer != NULL) {
length ++;
pointer = pointer->next;
}
return length;
}
2.带头结点和不带头结点链表的比较
获取链表长度:注意头结点是不存储数据的.
头插法:有头结点,更易操作.
3.复杂度分析
4.感受
①写完代码,并未感觉到两者有多大区别,一些边界条件可能需要改变一下,可以使用代码比较器https://www.matools.com/compare
比较一下这两份代码.
②在写不含头结点的链表的时候,因为我决定在插入操作的时候进行初始化,所以存在一个问题:如何将初始化好的链表返还给在main方法中定义的LinkedList* list
?
如果直接传递该指针给新增Node节点函数会发现,参数指针指向新节点,并不能使main方法中的指针list指向这片内存。
因为在c语言中参数传递是值传递,不像java中引用类型是引用传递,所以使用了c++中的引用,其实也可以使用二维指针,也就是指向指针的指针,不过引用使用起来更加方便。
我是用的编译器是TDM-GCC 4.8.1 64bit
。
- 双链表、循环链表
1.带头结点的循环双链表实现:
#include
#include
/**
* 定义双向链表的节点.
* prior 前驱指针.
* data 数据域.
* next 后继指针.
*/
typedef struct DoubleList{
struct DoubleList* prior;
int data;
struct DoubleList* next;
}DoubleList,Node;
/**
* 遍历顺序枚举.
* sequential 顺序.
* reverse 逆序.
**/
typedef enum order{
sequential,
reverse
} Order;
/**
* 初始化双向链表.
* @return list 双向链表.
*/
DoubleList* initDoubleList();
/**
* 获取双向链表节点.
* @param data 数据.
* @return 双向链表节点.
*/
Node* getNode(int data);
/**
* 展示双向链表元素.
* @param data 数据.
* @parma showOrder 顺序.
* @return 双向链表节点.
*/
void showDoubleList(DoubleList* list, Order showOrder);
/**
* 尾插法.
* @param list 链表.
* @param data 数据.
* @return true 插入成功,false失败.
*/
bool tailInsertNode(int data,DoubleList* list);
/**
* 头插法.
* @param list 链表.
* @param data 数据.
* @return true 插入成功,false失败.
*/
bool headInsertNode(int data,DoubleList* list);
/**
* 指定位置新增.
* @param list 链表.
* @param data 数据.
* @param position 位置.
* @return true 插入成功,false失败.
*/
bool positonInsertNode(int data,int position,DoubleList* list);
/**
* 指定位置删除.
* @param list 链表.
* @param data 数据.
* @param position 位置.
* @return true 删除成功,false失败.
*/
bool positionDeleteNode(int position,DoubleList* list);
/**
* 获取长度.
* @param list 链表.
* @return 长度.
*/
int getLength(DoubleList* list);
/**
* 根据位置找节点.
* @param position 位置.
* @param list 双链表.
* @return 节点.
*/
Node* getNodeByPosition(int position,DoubleList* list);
int main () {
DoubleList* list = initDoubleList();
tailInsertNode(1,list);
tailInsertNode(2,list);
tailInsertNode(3,list);
tailInsertNode(4,list);
tailInsertNode(5,list);
tailInsertNode(6,list);
headInsertNode(7,list);
headInsertNode(8,list);
positonInsertNode(9,1,list);
positionDeleteNode(1,list);
}
DoubleList* initDoubleList(){
Node* node = getNode(0);
node->next = node;
node->prior = node;
if(node == NULL){
puts("初始化失败");
}
return node;
}
Node* getNode(int data) {
Node* node = (Node*) malloc(sizeof(Node));
if(node == NULL) {
puts("获取节点失败");
return node;
}
node->data = data;
node->prior = NULL;
node->next = NULL;
return node;
}
void showDoubleList(DoubleList* list,Order order){
switch(order) {
// 顺序遍历.
case sequential:{
DoubleList* pointer = list->next;
while(pointer != list) {
printf("%d ",pointer->data);
pointer = pointer->next;
}
printf("\n");
break;
}
//逆向遍历.
case reverse:{
DoubleList* pointer = list->prior;
while(pointer != list){
printf("%d ",pointer->data);
pointer = pointer->prior;
}
printf("\n");
break;
}
default:
puts("遍历顺序有误,请检查");
}
}
bool tailInsertNode(int data,DoubleList* list){
Node* node = getNode(data);
DoubleList* pointer = list->prior;
node->prior = pointer;
pointer->next = node;
list->prior = node;
node->next = list;
Order order = sequential;
showDoubleList(list,order);
return true;
}
int getLength(DoubleList* list) {
int length = 0;
DoubleList* pointer = list->next;
while(pointer != list){
length++;
pointer = pointer->next;
}
return length;
}
bool headInsertNode(int data,DoubleList* list) {
Node* node = getNode(data);
node->next = list->next;
list->next->prior = node;
list->next = node;
node->prior = list;
Order order = sequential;
showDoubleList(list,order);
return true;
}
bool positonInsertNode(int data,int position,DoubleList* list) {
Node* targetNode = getNodeByPosition(position,list);
Node* insertNode = getNode(data);
insertNode->next = targetNode;
targetNode->prior->next = insertNode;
insertNode->prior = targetNode->prior;
targetNode->prior = insertNode;
Order order = sequential;
showDoubleList(list,order);
return true;
}
Node* getNodeByPosition(int position,DoubleList* list) {
if(position < 1 || position > getLength(list)){
puts("illegal position");
return NULL;
}
DoubleList* pointer = list;
for(int i = 0; i< position; i++) {
pointer = pointer->next;
}
return pointer;
}
bool positionDeleteNode(int position,DoubleList* list) {
if(position < 1 || position > getLength(list)){
puts("illegal position");
return false;
}
Node* node = getNodeByPosition(position,list);
node->prior->next = node->next;
node->next->prior = node->prior;
free(node);
Order order = sequential;
showDoubleList(list,order);
return true;
}
2.复杂度分析
遍历:O(n)
头插:O(1)
尾插:O(1)
指定位置插入/删除:O(n) 找位置需要遍历
指定节点插入/删除:O(1)
3.注意事项
①循环双链表初始化时前后指针都指向自己.
②遍历循环双链表的结束条件,以及遍历顺序.
③头插尾插指针指向的处理.
④知道头很容易找到尾,知道尾也就知道了头.
...
4.写完代码的自我感受
感觉循环双链表写起来更方便,尤其是在指定节点的头插入和删除操作中,弥补了单链表中找当前节点前驱节点需要遍历链表的问题.虽然多了一个前驱指针,需要消耗一定的空间,但是从整体来说,牺牲一点空间来换取操作的遍历,在空间资源不紧张的情况下,我认为是十分合理的,在双链表的基础上,增加了循环,这样知道任意一个节点都可以遍历整个表,而且在处理最后一个节点的问题上,永远不用担心空指针异常。
- 静态链表
1.带头结点的静态单链表实现
#include
#define MAX_SIZE 10
/**
* 给匿名结构体重新命名,新名是个数组.
* 通过 StaticList list可以定义一个
* 大小为MAX_SIZE 的结构体数组.
*/
typedef struct Node{
int data;
int next;
} StaticList,Node;
/**
* 初始化一个静态链表.
* @return 静态链表.
*/
StaticList* initStaticList();
/**
* 展示静态数组元素.
* @param list 指向静态链表的指针.
*/
void showStaticList(StaticList* list);
/**
* 尾插.
* @param data 数据.
* @param list 静态链表指针.
*/
bool tailInsert(int data,StaticList* list);
/**
* 头插.
* @param data 数据.
* @param list 静态链表指针.
*/
bool headInsert(int data,StaticList* list);
/**
* 指定位置插入.
* @param data 数据.
* @param postition 位置.
* @param list 静态链表指针.
* @return true成功,false失败.
*/
bool poasitionInsert(int data,int position,StaticList* list);
/**
* 指定位置删除.
* @param data 数据.
* @param postition 位置.
* @param list 静态链表指针.
* @return true成功,false失败.
*/
bool positionDelete(int position,StaticList* list);
/**
* 获取一个可以使用的静态链表index.
* @return index.
*/
int getNode(StaticList* list);
/**
* 获取指定位置的节点指针.
* @param position 位置.
* @param list 链表.
* @return Node指针.
*/
Node* getNodeByPosition(int position,StaticList* list);
/**
* 获取链表长度.
* @param list 链表.
* @return 长度.
*/
int getlength(StaticList* list);
int main() {
StaticList* list = initStaticList();
tailInsert(1,list);
tailInsert(2,list);
tailInsert(3,list);
headInsert(4,list);
headInsert(5,list);
headInsert(6,list);
poasitionInsert(7,6,list);
poasitionInsert(8,6,list);
positionDelete(8,list);
}
StaticList* initStaticList() {
static StaticList list[MAX_SIZE];
// -2表示可以使用.
for(int i = 0; i < MAX_SIZE; i++) {
list[i].data = 0;
list[i].next = -2;
}
list[0].next = -1;
return list;
}
void showStaticList(StaticList* list) {
Node* pointer = &list[list[0].next];
while(pointer->next != -1) {
printf("%d ",pointer->data);
pointer = &list[pointer->next];
}
printf("%d\n",pointer->data);
}
bool tailInsert(int data,StaticList* list) {
Node* pointer = list;
while(pointer->next != -1) {
pointer = &list[pointer->next];
}
int index = getNode(list);
if(index == -3) {
return false;
}
pointer->next = index;
list[index].data = data;
list[index].next = -1;
showStaticList(list);
return true;
}
int getNode(StaticList* list) {
Node* pointer = list;
for(int i =0; inext;
list->next = index;
showStaticList(list);
return true;
}
bool poasitionInsert(int data,int position,StaticList* list) {
if(position < 1 || position > MAX_SIZE) {
puts("position illegal");
return false;
}
Node* node = getNodeByPosition(position,list);
int index = getNode(list);
list[index].next = node->next;
list[index].data = data;
node->next = index;
//交换值.
int temp = list[index].data;
list[index].data = node->data;
node->data = temp;
showStaticList(list);
return true;
}
Node* getNodeByPosition(int position,StaticList* list) {
if(position < 1 || position > MAX_SIZE) {
puts("position illegal");
return NULL;
}
StaticList* pointer = list;
for(int i = 0; i < position;i++) {
pointer = &list[pointer->next];
}
return pointer;
}
bool positionDelete(int position,StaticList* list) {
if(position < 1 || position > MAX_SIZE) {
puts("position illegal");
return false;
}
int length = getlength(list);
//单独处理最后一个元素.
if(position == length) {
StaticList* pointer = list;
for(int i = 0; i < position -1;i++) {
pointer = &list[pointer->next];
}
Node* node = &list[pointer->next];
pointer->next = -1;
node->data = 0;
node->next = -2;
showStaticList(list);
return true;
}
Node* node = getNodeByPosition(position,list);
Node* nextNode = &list[node->next];
node->data = list[node->next].data;
node->next = list[node->next].next;
nextNode->data = 0;
nextNode->next = -2;
showStaticList(list);
return true;
}
int getlength(StaticList* list) {
int length;
StaticList* pointer = list;
while(pointer->next != -1) {
length++;
pointer = &list[pointer->next];
}
return length;
}
2.复杂度分析
头插 :获取可用节点O(n),实际插入O(1),整体O(n)
尾插: 获取可用节点O(n),获取最后一个节点O(n),实际插入O(1),整体O(n)
指定位置插入:获取可用节点O(n),获取指定位置节点O(n),实际插入O(1),整体O(n)
遍历:O(n)
3.注意事项
①指针的移动方式
②如何判断遍历到最后一个节点
③节点的释放
④可使用节点的获取
⑤单链表通病最后一个元素的删除问题
4.写完代码的感受
静态链表实际上是一个使用结构数组来实现的,某些不支持指针的语言,可以使用结构数组来实现链表的功能,但是因为一开始就指定了结构数组的大小,所以需要扩容操作,扩容操作就意味着需要进行元素的拷贝,元素大且多的情况下,元素拷贝是很费时间的。静态链表还是有它的局限性的。
- 顺序表和链表的对比
1.空间性能的比较
线性表长度变化大,难以预估存储规模,用链表
线性表长度变化不大,能事先确定存储大小,用顺序表
①存储空间的分配
顺序表的存储空间必须预先分配,元素个数扩充受限,易造成存储空间浪费或空间溢出现象;
链表无需预先分配空间,内存空间允许时,元素个数无限制。
②存储密度的大小
不考虑顺序表中的空闲区,顺序表存储空间利用率为100%,存储密度为1;
链表存储空间利用率小于100%,存储密度小于1,单链表存储密度为0.5。
长度变化不大,且事先确定存储大小,采用顺序表可节约存储空间。
2.时间性能的比较
很少查找,频繁插入或删除,用链表
频繁查找,很少插入或删除,用顺序表
①存取元素的效率
顺序表是由数组实现的随机存取结构,根据位置序号实现取值操作,效率高 时间复杂度O(1);
链表是顺序存取结构,从表头开始依次向后遍历链表,取值效率底 时间复杂度O(n)。
②插入和删除操作的效率
链表插入或删除无需移动数据,只修改指针,时间复杂度为O(1);
顺序表插入或删除时,平均要移动表中近一半的结点,时间复杂度为O(n)。
备注:
代码是自己一行行写的,难免存在疏漏bug,还请大家批评指正,谢谢。
写文章的目的是为了加深对知识和代码的理解,方便后续复习,2022考研加油!!!