顺序表&链表

1.线性表

  • 概念:有n(n >= 0)个数据元素组成的有限序列
  • 性质:
    1)可以在其任意位置上进行插入和删除操作的线性数据结构
    2)线性表中的数据存在一对一的关系,即除第1个元素和最后一个元素外,每个元素都有唯一的直接前驱和后继。而第1个元素没有前驱,最后一个元素没有后继。

2.顺序表

  • 概念:用一段地址连续的存储单元依次存储数据元素的线性结构
  • 模型图:
    这里写图片描述
    动态顺序表的实现:
#pragma once

typedef int DataType;

typedef struct SeqList
{
    DataType* _a;
    size_t _size;//当前的有效数据个数
    size_t _capacity;//容量:有多大空间
}SeqList;

void SeqListDestroy(SeqList* pSeq)
{
    assert(pSeq);
    free(pSeq->_a);
    pSeq->_a = NULL;
    pSeq->_size = pSeq->_capacity = 0;
}

void SeqListInit(SeqList* pSeq, size_t capacity)
{
    assert(pSeq && capacity > 0);
    pSeq->_a = (DataType*)malloc(capacity*sizeof(DataType));
    assert(pSeq->_a);

    pSeq->_size = 0;
    pSeq->_capacity = capacity;
}

void SeqListPrint(SeqList* pSeq)
{
    size_t i = 0;
    for (; i < pSeq->_size; ++i)
    {
        printf("%d", pSeq->_a[i]);
    }
    printf("\n");
}

void CheckCapacity(SeqList* pSeq)
{
    if (pSeq->_size >= pSeq->_capacity)
    {
        DataType* a = (DataType*)relloc(pSeq->_a, sizeof(DataType)*pSeq->_capacity * 2);
        assert(a);
        pSeq->_a = a;
        pSeq->_capacity *= 2;
    }
}//此处在检查到顺序表的容量不够时,扩容使用的方法是将之前容量扩大2倍,让其动态增长

void SeqListPushBack(SeqList* pSeq, DataType x)//尾插
{
    assert(pSeq);
    CheckCapacity(&pSeq);

    pSeq->_a[pSeq->_size] = x;
    pSeq->_size++;
}

void SeqListPushFront(SeqList* pSeq, DataType x)//头插
{
    //assert(pSeq);
    //if (pSeq->_size >= N)
    //{
    //  printf("SeqList is full\n");
    //}
    //else
    //{
    //  int end = pSeq->_size - 1;
    //  while (end >= 0)
    //  {
    //      pSeq->_a[end + 1] = pSeq->_a[end];
    //      --end;
    //  }

    //  pSeq->_a[0] = x;
    //  pSeq->_size++;
    //}
    SeqLisrtEarse(pSeq, 0);
}

void SeqListPopFront(SeqList* pSeq)//头删
{
    /*int i = 1;
    assert(pSeq);
    for (; i < pSeq->_size; ++i)
    {
    pSeq->_a[i - 1] = pSeq->_a[i];
    }

    --pSeq->_size;*/
    SeqLisrtEarse(pSeq, 0);
}

void SeqListPopBack(SeqList* pSeq)//尾删
{
    //assert(pSeq);
    if (pSeq->_size == 0)
    {
        printf("SeqList is empty\n");
        return;
    }
    //else
    //{
    //  //pSeq->_a[pSeq->_size + 1] = 0;
    //  --pSeq->_size;
    //}
    SeqLisrtEarse(pSeq, pSeq->_size - 1);
}

void SeqListInsert(SeqList* pSeq, size_t pos, DataType x)//指定位置插入
{
    assert(pSeq);
    assert(pos <= pSeq->_size);

    if (pSeq->_size >= N)
    {
        printf("SeqList is full\n");
    }
    else
    {
        //会越界,因为类型提升(两个不同类型的数据作比较时,范围小的向范围大的改):int->size_t
        int end = pSeq->_size - 1;
        for (; end >= (int)pos;)
        {
            pSeq->_a[end + 1] = pSeq->_a[end];
            --end;
        }

        pSeq->_a[pos] = x;
        pSeq->_size++;
    }
}

void SeqLisrtEarse(SeqList* pSeq, size_t pos)//指定位置删除
{
    assert(pSeq);
    assert(pos < pSeq->_size);

    if (pSeq->_size == 0)
    {
        printf("SeqList is empty\n");
    }
    else
    {
        int i = pos;
        while (i < pSeq->_size - 1)
        {
            pSeq->_a[i] = pSeq->_a[i + 1];
            ++i;
        }
        pSeq->_size--;
    }
}

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

void SeqListModify(SeqList* pSeq, size_t pos, DataType x)//修改pos位置的值为x
{
    assert(pSeq && pos < pSeq->_size);
    pSeq->_a[pos] = x;
}

//index和i作为下标都指向第一个数,如果index指向的这个数不是x,就把它给给i这个位置,第一个数如果不是x就相当于自己给自己不动,
//接着index和i向后移,如果index指向的数是x,i就不动,接着index继续向后移,如果index指向的值不是x,把这个值给给i这个位置
//接着i++往后走,index++也往后走,重复上面的步骤
void SeqListRemoveAll(SeqList* pSeq, DataType x)//删除数组中所有值为x的数
{
    size_t index = 0, i = 0, count = 0;
    assert(pSeq);
    for (; i < pSeq->_size; ++i)
    {
        if (pSeq->_a[i] != x)
        {
            pSeq->_a[index] = pSeq->_a[i];
            ++index;
        }
        else
        {
            count++;
        }
    }
    pSeq->_size -= count;
}

void SeqListBubbleSort(SeqList* pSeq)
{
    int exchange = 0;
    int end = pSeq->_size;//end是最后冒到的位置
    assert(pSeq);

    while (end > 1)
    {
        int i = 0;
        //单趟排序,冒到end之前
        for (i; i < end; ++i)
        {
            if (pSeq->_a[i - 1] > pSeq->_a[i])
            {
                Swap(&pSeq->_a[i - 1], &pSeq->_a[i]);
                exchange = 1;
            }
        }
        if (exchange == 1)
            break;

        --end;
    }
}

void SeqListSelectSort(SeqList* pSeq)
{

}
//分两种情况:闭区间和左闭右开区间
//void SeqListBinarySearch(SeqList* pSeq,DataType x)
//{
//  int left = 0, right = pSeq->_size - 1;
//  assert(pSeq);
//
//  while (left  <= right)//闭区间:当区间只有一个数时,如果是<就不会进入while循环,也就找不到这个数
//  {
//      int mid = left + (right - left) >> 1;
//      if (pSeq->_a[mid] <= x)
//          left = mid + 1;//此时mid这个位置已经被找过,要+1才能继续使它成为闭区间
//      else if (pSeq->_a[mid] > x)
//          right = mid - 1;
//      else
//          return mid;
//  }
//  return -1;
//}

//左闭右开区间
void SeqListBinarySearch(SeqList* pSeq, DataType x)
{
    int left = 0, right = pSeq->_size;
    assert(pSeq);

    while (left < right)//如果用=,right这个位置上的数不存在发生越界
    {
        int mid = left + (right - left) >> 1;
        if (pSeq->_a[mid] <= x)
            left = mid + 1;
        else if (pSeq->_a[mid] > x)
            right = mid;
        else
            return mid;
    }
    return -1;
}
  • 数组与顺序表的比较:
    1)数组就是相同数据类型的元素按一定顺序排列的集合。也就是说物理上存储在一组连续的地址上,称为数据结构中的物理结构。
    2)线性表是数据结构中的逻辑结构,可以存储在数组上,也可以存储在链表上。用顺序存储方法存储的线性表称为顺序表。
    总的来说,用数组来存储的线性表就是顺序表。

3.链表

  • 概念:链表是线性表的一种,但它不是顺序表。链表的每个节点里都存储着指向下一个节点的指针,把存储数据元素的数据串联起来。根据指针的不同,链表有单链表、双链表和循环链表之分。
  • 模型图:
    这里写图片描述
    1)单链表是只包含指向下一个节点的指针,只能单向遍历。
    2)双链表既包含了指向下一个节点的指针,又包含了指向前一个节点的指针,因此可以双向遍历
    3)单向循环链表是将单链表的尾节点和首节点串起来,形成一个环状结构
    4)双向循环链表是将双向链表的尾节点和头节点串起来,形成环状结构
    单链表的实现:
#pragma once

typedef int DataType;
typedef struct SListNode
{
    struct  SListNode* _next;
    DataType           _data;
}SListNode;

SListNode* BuySListNode(DataType x)//创建节点
{
    SListNode* node = (SListNode*)malloc(sizeof(SListNode));
    assert(node);
    node->_data = x;
    node->_next = NULL;

    return node;
}

void SListPushBack(SListNode** ppHead, DataType x)
{
    if (*ppHead == NULL)
    {
        *ppHead = BuySListNode(x);
    }
    else
    {
        SListNode* cur = *ppHead;
        while (cur->_next != NULL)
        {
            cur = cur->_next;
        }
        cur->_next = BuySListNode(x);
    }
}

void SListPushFront(SListNode** ppHead, DataType x)
{
    if (*ppHead == NULL)
    {
        *ppHead = BuySListNode(x);
    }
    else
    {
        SListNode* newNode = BuySListNode(x);
        newNode->_next = *ppHead;
        *ppHead = newNode;
    }
}

void SListPopBack(SListNode** ppHead)
{
    //1.空
    //2.一个节点
    //3.多个节点
    if (*ppHead == NULL)
        return;

    else if ((*ppHead)->_next == NULL)
    {
        free(*ppHead);
        *ppHead = NULL;
    }
    else
    {
        SListNode* prev = NULL, *cur = *ppHead;
        while (cur->_next)
        {
            prev = cur;
            cur = cur->_next;
        }

        free(cur);
        prev->_next = NULL;
    }
}

// 先保存头结点的下一个节点,再删去头结点
void SListPopFront(SListNode** ppHead)
{
    if (*ppHead == NULL)
        return;

    else
    {
        SListNode* next = (*ppHead)->_next;
        free(*ppHead);
        *ppHead = next;
    }
}

void SListDestroy(SListNode** ppHead)
{
    SListNode* cur = *ppHead;
    while (cur)
    {
        SListNode* next = cur->_next;//先保存下一个节点,防止释放时野指针
        free(cur);
        cur = next;
    }

    *ppHead = NULL;
}

void SListPrint(SListNode* pHead)
{
    SListNode* cur = pHead;
    while (cur)
    {
        printf("%d->", cur->_data);
        cur = cur->_next;
    }
    printf("NULL\n");
}

SListNode* SListFind(SListNode* pHead, DataType x)
{
    SListNode* cur = pHead;
    while (cur)
    {
        if (cur->_data == x)
        {
            return cur;
        }
        cur = cur->_next;
    }

    return NULL;
}

void SListInsert(SListNode** ppHead, SListNode* pos, DataType x)//在pos前插入x
{
    assert(pos && *ppHead);
    //1.中间:找到pos前一个位置,创建节点再互相链起来
    //2.头
    //尾插和在中间插入没有区别
    if (pos == *ppHead)
    {
        SListPushFront(ppHead, x);//ppHead是外面list指针的地址,ppHead解引用改变list的地址,如果把pos的地址传过去,改的是pos,但外面的指针没改
    }
    else
    {
        SListNode* newnode = BuySListNode(x);
        SListNode* prev = *ppHead;
        while (prev->_next != pos)
        {
            prev = prev->_next;
        }

        //prev newnode pos
        prev->_next = newnode;
        newnode->_next = pos;
    }
}
void SListEarse(SListNode** ppHead, SListNode* pos)
{
    assert(pos && *ppHead);

    if (*ppHead == pos)
    {
        SListPopFront(ppHead);
    }
    else if (pos->_next == NULL)
    {
        SListPopBack(ppHead);
    }
    else
    {
        SListNode* prev = *ppHead;
        while (prev->_next != pos)
        {
            prev = prev->_next;
        }

        prev->_next = pos->_next;//先让pos的前一个和后一个链起来,再删掉pos
        free(pos);
    }
}
双向链表的实现:
#pragma once

typedef int DataType;

typedef struct DListNode
{
    struct DListNode* _next;
    struct DListNode* _prev;
    DataType          _data;
}DListNode;

DListNode* BuyDListNode(DataType x)
{
    DListNode* node = (DListNode*)malloc(sizeof(DListNode));
    assert(node);

    node->_next = NULL;
    node->_prev = NULL;
    node->_data = x;

    return node;
}

DListNode* DListInit();
DListNode* DListDestroy(DListNode* head);
DListNode* DListPrint(DListNode* head);

void DListPushBack(DListNode* head, DataType x);
void DListPushFront(DListNode* head, DataType x);
void DListPopBack(DListNode* head);
void DListPopFront(DListNode* head);

DListNode* DListFind(DListNode* head, DataType x);
void DListInsert(DListNode* pos, DataType x);
void DListErase(DListNode* pos);

DListNode* DListPrint(DListNode* head)
{
    DListNode* cur = head->_next;
    while (cur != head)
    {
        printf("%d->", cur->_data);
        cur = cur->_next;
    }
    printf("HEAD\n");
    return NULL;
}

DListNode* DListInit()
{
    DataType x = 0;
    DListNode* list = BuyDListNode(x);
    list->_next = list;
    list->_prev = list;

    return list;//带头双向循环链表
}

DListNode* DListDestroy(DListNode* head)
{
    assert(head);
    DListNode* cur = head->_next;//当tail==head时结束遍历,所以不能是cur=head
    while (cur != head)
    {
        DListNode* next = cur->_next;
        free(cur);
        cur = next;
    }

    free(head);
    return head;
}

void DListPushBack(DListNode* head, DataType x)
{
    //assert(head);
    //DListNode* tail = head->_prev;
    //DListNode* newnode = BuyDListNode(x);

    tail newnode tail
    //tail->_next = newnode;
    //newnode->_prev = tail;
    //newnode->_next = head;
    //head->_prev = newnode;
    DListInsert(head, x);
}

void DListPushFront(DListNode* head, DataType x)
{
    //assert(head);
    //DListNode* first = head->_next;
    //DListNode* newnode = BuyDListNode(x);

    head   newnode  first 
    //head->_next = newnode;
    //newnode->_prev = head;
    //newnode->_next = first;
    //first->_prev = newnode;
    DListInsert(head->_next, x);
}

void DListPopBack(DListNode* head)
{
    //assert(head);
    //DListNode* tail = head->_prev;
    //DListNode* prev = tail->_prev;

    head prev tail
    //prev->_next = head;
    //head->_prev = prev;

    //free(tail);

    DListErase(head->_prev);
}

void DListPopFront(DListNode* head)
{
    //assert(head);
    //if (head->_next = head)
    //  return;

    //DListNode* first = head->_next;
    //DListNode* next = first->_next;

    head first next
    //head->_next = next;
    //next->_prev = head;

    //free(first);

    DListErase(head->_next);
}

DListNode* DListFind(DListNode* head, DataType x)
{
    DListNode* cur = head->_next;
    while (cur != head)
    {
        if (cur->_data == x)
        {
            return cur;
        }
        cur = cur->_next;
    }
    return NULL;
}

void DListInsert(DListNode* pos, DataType x)//在pos前插入
{
    assert(pos);
    DListNode* prev = pos->_prev;
    DListNode* newnode = BuyDListNode(x);

    //prev newnode pos
    prev->_next = newnode;
    newnode->_prev = prev;
    newnode->_next = pos;
    pos->_prev = newnode;
}

void DListErase(DListNode* pos)
{
    //不能删头结点
    assert(pos);

    DListNode* prev = pos->_prev;
    DListNode* next = pos->_next;

    next->_prev = prev;
    prev->_next = next;

    free(pos);
}
  • 顺序表和链表比较:
    1)顺序表的长度固定,必须在分配内存之前确定数组的长度;链表的长度不固定,可以任意删减
    2)顺序表的存储空间连续,支持元素的随机访问;链表的存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素;
    3)顺序表的存储密度大,内存中存储的全部是数据元素;而链表的存储密度小,每个节点都需要二外存储一个指向下一个节点的指针(双链表则需要两个指针)
    4)顺序表在访问特定元素时效率较高,可以使用索引访问,时间复杂度为O(1);链表在访问特定元素时,需要从表头开始,遍历到该元素,时间复杂度为O(n)
    5)顺序表在进行插入/删除操作时,会涉及到从当前位置到表尾元素的移动,时间复杂度为O(n);链表在进行插入/删除操作时不需要移动元素,因此时间复杂度为O(1)
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值