数据结构之静态顺序表

title: 数据结构之静态顺序表
date: 2018-11-09 14:21:51
tags: C-数据结构

静态顺序表属于数据结构开始的一种基本结构

首先我们要知道数据结构的概念

  1. 数据结构 数据的组织关系
  2. 算法 为了达到特定的目的的一系列过程。在这个过程中又分为两种角度
    1) 逻辑角度:线性结构,树形结构,图形结构
    2) 存储角度:顺序存储,链式存储

在线性结构中顺序存储的方式,在本次中为顺序表,分为静态和动态

而静态顺序表的理解可以分为

1) 结构体定义(定义、背后的内存布局模型)

2) 顺序表的基本操作:插/删/查/改。重点需要去掌握的是插和删。插(头插/尾插/插入),删(头删/尾删/删除)

接下来开始对于整个代码进行一个分析。

    #pragma once
    #include<stdio.h>
    #include<stdlib.h>   //一般不着急于先定义一堆,你需要什么在去定义什么。
    
    typedef int DataType;  //为现有类型创建别名。用DataType来代替int。
    
    #define MAX_SIZE(100) //开出一块空间,占用内存的最大位置。
    
    typedef struct SeqList{     //定义结构体的别名
    	DataType array[MAX_SIZE];  
    	int size;
    }SeqList;   //变量名

当把结构体都定义好了之后,我们开始先进行两个基本操作,对数组进行初始化和销毁

此时我们要用到assert()。在开头首先要定义这个宏#include<assert.h>,他的功能就是测试一个条件可能使程序终止。用法void assert(int test)

在初始过程中,可能需要把内存清空一下,所以我们可能需要memset()这个函数。这个函数的用法void *memset(void *s, int ch, size_t n);

将s所指向的某一块内存中的后n个 字节的内容全部设置为ch指定的ASCII值,第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作,其返回值为s。

    //初始化
    void SeqListInit(Seqlist *pSeq){
    	//初始化size
    	assert(pSeq != NULL);
    	pSep->size = 0;
    	memset(pSep->array,0,SIZE_MAX * sizeof(DataType));
    }
    //销毁
    void SeqListDestory(SeqList *pSeq){
    	assert(pSeq);
    	pSeq->size = 0;
    }

我们还需要部署一个测试函数,先暂时定义一下,当增删改查部署完后,可以往其中添加增删改查的函数来进行实验。

插入:有头插,尾插,插入

头插的使用

PS:考虑完了普通情况后,我们要考虑的是特殊情况,当size超过了定义的最大尺寸情况,我们需要进行一个判断,

    void SeqListPushTop(SeqList *pSeq,DataType data){
        assert(pSeq);
        if(pSeq->size >= MAX_SIZE){
            printf("满了\n");
            assert(0);
            return;
        }
        //从最后开始向每一个后面开始搬运,如果不这么做,将会将每一次的值覆盖掉
        for(int i = pSeq->szie;i > 0; i--){  // i此时是定义的为位置,位置为[size,0],从后往前搬
            pSeq->array[i] = pSeq->array[i - 1];
        }
        //将i定位为数据,位置size[0,size),将整体的位置向后移
        for(int i = size - 1;i > 0;i++){
            pSeq->array[i + 1] = pSeq->array[i];
        }
        pSeq->array[0]=data;
        pSeq->size++;
    }

尾插的使用

尾插就相当于正常插入了,这个是比较简单的一种插入

    void SeqListPushBack(SeqList *pSeq,DataType data){
        //还是要先考虑特殊情况
        assert(pSeq);
        if(pSeq->size >= MAX_SIZE){
            printf("满了\n");
            assert(0);
            return;
        }
        pSeq->array[pSeq->size] = data;
        pSeq->size++;
    }

中间插入的使用

中间插入根据下标来使用,此时这么想,我们先将要插入的地方定义一个pos下标,此时将此下标之后的(包括此下标的)所有数据向后移动。

void SeqListInsert(SeqList *pSeq,int pos, DataType data){
    //考虑特殊情况
        assert(pSeq);
    if(pSeq->size >= MAX_SIZE){
        printf("满了\n");
        assert(0);
        return;
        //此时i作为位置
        for(int i = pSeq->size; i >= pos; i--){
            pSeq->array[i] = pSeq->array[i - 1];
        }
        //此时i作为数据
        for(int i = pSeq->size - 1;i >= pos; i--){
            pSeq->array[i + 1] = pSeq->array[i]; 
        }
        pSeq->array[pos] = data;
        pSeq->szie++;
}

删除:头删,尾删,删除

头删的使用

最开始的数删除后,其他的数据向前补进。当然,也别忘了特殊情况!

void SeqListPushFont(SeqList *pSeq){
    //特殊情况
    assert(pSeq)
        if(pSeq->size <= 0){
            printf("空了\n");
            assert(0);
            return;
        }
    //i此时为位置时
    for(int i = 0; i < pSeq->size - 1;i++){
        pSeq->array[i] = pSeq->array[i + 1];
    }
    //i此时为数据时
    for(int i = 1;i < pSeq->size;i++){
        pSeq->array[i - 1] = pSeq->array[i];
    }
    pSeq->size--;
}

尾删的使用

尾删比较简单,也就是直接将最后一个删除

void SeqListPushPop(SeqList *pSeq){
    assert(pSeq)
        if(pSeq->size <= 0){
            printf("空了\n");
            assert(0);
            return;
        }
    pSeq->size--;//直接将空间减小
}

删除的使用

删除从中间删除,还是定义一个pos下标,删除之后,在将所有数据向前移动

void SeqListPushErase(SeqList *pSeq,int pos){
        assert(pSeq)
        if(pSeq->size <= 0){
            printf("空了\n");
            assert(0);
            return;
        }
    for(int i = pos;i <= pSeq->size ;i--){
        pSeq->array[i - 1] = pSeq->array[i];
    }
}

当我们基本的这些操作编写完之后,我们需要输出这些数组,来观察。

void SeqListPrint(const SeqList *pSeq){
    assert(pSeq != NULL);
    for(int i = 0;i < pSeq->size;i++){
        printf("%d",pSeq->array[i]);
    }
    printf("\n");
}

删除的第二种形态

用接口的方式来实现删除。

接口可以理解为封装一个函数来对其使用。这样我们就可以有一些其他的删除方式

删除第一个遇到的数据的形式

    void SeqListRemove(SeqList *pSeq, DataType data){
        int pos = SeqListFind(pSeq,data);
        if(pos == -1){
            //找不到删除
            return;
        }
        SeqListPushErase(pSeq, pos);
    }

删除遇到的所有数据的形式

    void SeqListRemoveALL(SeqList *pSeq,DataType data){
        //第一种方式
        while(pos = SeqListFind(pSeq,data) != -1){
            SeqListPushErase(pSeq,data);
        }
    }

    void SeqListRemovALL(SeqList *pSeq,DataType data){
        //第二种方式
        //一次遍历空间,时间快,但是开辟了新空间,空间大小和size有关系
        //开辟一个新空间,遍历原来的数组
        //if arr[i] != data
        //new [j+1]= arr[j+1]
        //else i++
        //把数据在搬回来,还剩的数据个数为i个
        DataType *newArray = (DataType *)malloc(sizeof(DataType)*pSeq->size);
        int i, j;
        for(i = 0,j = 0;i < pSeq->size;i++){
            if(data != pSeq->array[i]){
                newArray[j] = pSeq->array[i];
                j++
            }
        }
        for(i = 0;i < j; i++){
            pSeq->array[i] = newArray[i];
        }
        pSeq->size = j;
        free(newArray);
    }

    void SeqListRemoveALL(SeqList *pSeq,DataType data){
        //第三种方法
        //这个方法没有开辟新的空间,直接对其每个所对应的新区域赋值
        //这样我们最后将数组size直接改为新开辟的大小
        int i, j;
        for(i = 0,j = 0;i < pSeq->size, i++){
            pSeq->array[j] = pSeq->array[i];
            j++;
        }
        pSeq->size = j;
    }

查找的使用

查找也是一个关键的选择,因为,在查找到我们需要的下标之后,我们可以对此进行各种增删改操作

查找也有很多方式,这里先仅做一种顺序遍历查找。查找也有二分查找(前提有序)

int SeqListFind(SeqList *pSeq,DataType data){
    for(int i = 0;i < pSeq->size;i++){
        if(data = pSeq->size[i]){
            return 1;
        }
        return -1;
    }
}

一些其他的使用操作

对于顺序表的操作我们还有一些其他的操作

int SeqListSize(SeqList *pSeq){
    //查看当前size的大小
    return pSeq->size;
}

int SeqListEmpty(SeqList *pSeq){
    //清空当前数组
    return pSeq->size == 0;
}

int SeqListFull(SeqList *pSeq){
    //将数组的size放到最大
    return pSeq->size == MAX_SIZE;
}

void Swap(DataType *a,DataType *b){
    //交换两个数据
    DataType t = *a;
    *a = *b;
    *b = t;
}

改的使用

我们可以理解为,我们将要怎么让这个数组去改变,那么我们可以使用排序的方式将数组发生变化。这是一种改法。在静态顺序表中。通过举冒泡排序的例子来使用。

void SeqListBubbleSort(SeqList *pSeq){
    int i,j;
    int isSort;
    for(i = 0;i < pSeq->size - 1;i++){
        isSort = 1;
        for(j = 0;j < pSeq->size - 1 - i; j++){
            if(pSeq->array[i] > pSeq->array[i + j]){
                Swap(pSeq->array + j, pSeq->array + j + 1)
            }
            isSort = 0;
        }
        if(isSort){
            break;
        }
    }
}

在这里调用冒泡排序的方法是可以进行数组的改变。

以上就是关于静态顺序数组的基本组成。总的来说,数据结构的难点在于思想过程。怎么把在纸上我们通过绘画的东西,利用代码的形式去表达出来,这是一个很关键的思考过程。

猜你喜欢

转载自blog.csdn.net/skrskr66/article/details/83956431
今日推荐