考研数据结构之线性表(C语言实现)

小黄鸭(来自网络)
一、线性表 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考研加油!!!

你可能感兴趣的:(考研数据结构之线性表(C语言实现))