数据结构之线性表(手把手带你刷OJ)

数据结构之线性表(手把手带你刷OJ)

线性表的定义:零个或多个数据元素的有限序列

​ 线性表在逻辑上是线性结构,就是连续的一条直线。但在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组链式结构的形式存储。

顺序表

顺序表存储结构

是一段地址连续的存储单元依次存储线性表的数据元素。一般采用数组来存储。

顺序表的两种实现方式

1.定长数组

​ 定长数组也就是静态数组,无法动态开辟内存。

缺点

  • 无法进行动态开辟内存。也就是当数据元素多时数据开劈的空间不够用,又或者是数据元素少时,又开劈太多的空间造成浪费。

在这里插入图片描述

2.动态数组

​ 动态数组是可以动态开劈内存,可以避免定长数组的劣势。动态数组开劈内存时一般选择1.5倍或者2倍。

动态数组的定义

顺序表的相关OJ题

  1. 27. 移除元素 - 力扣(LeetCode)

题解思路:采用双指针。两个指针一开始指向同一位置,当等于val值时,一个指针不动,另一个指针去找不等于val的值将其覆盖,并且两个指针同时++;

在这里插入图片描述

int removeElement(int* nums, int numsSize, int val) {
    
        if(nums == NULL)
            return NULL;

        int sub1 = 0;//下标1 
        int sub2 = 0;//下标2

         while(sub2 < numsSize ){

             //当下标2等于val值时,sub2++,
             if(nums[sub2] ==  val){
                 sub2++;
             }
             //当sub2二不等于val值时,覆盖sub1的val值,并且两个下标同时++
             else
             {
                 nums[sub1] = nums[sub2];
                 sub1++;
                 sub2++;
             }
         }
        return sub1;
}
  1. 26. 删除有序数组中的重复项 - 力扣(LeetCode)

解题思路:采用前后指针的思路。一个指针去找需要删除重复项的值,一个指针去找不需要删除重复项的值,先让后指针==再将其覆盖。

int removeDuplicates(int* nums, int numsSize) {

    if(nums == NULL)
        return nums;


    int sub1 = 0;//下标1 
    int sub2 = 1;//下标2
    while(sub2 < numsSize)
    {
        //当两个指针的值相同时,让前指针去找不等于当前指针的值。
        if(nums[sub1] == nums[sub2]){

            sub2++;
        }else{
		//当前指针找到不等于当前指针的值,赋值给后指针将其覆盖。
            sub1++;
            nums[sub1] = nums[sub2];
        }
    }    
    //由于函数调用接口是小于返回值sub1,因此要让sub1++;
    sub1++;
    return sub1;
}
  1. 88. 合并两个有序数组 - 力扣(LeetCode)

题解思路:双指针。一个指针指向数组1的有效元素位,另一个指针指向数组2的有效元素位。循环比较两个数组,数值大的尾插到数组1的后面。

在这里插入图片描述

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    //从数组有效元素下标开始

    int ptr1 = m-1;
    int ptr2 = n-1;
    int p = m+n-1;

    while( ptr1>=0 && ptr2>=0 )
    {
        //从数组的有效元素下标开始判断比较谁大,就尾插到nums1中
        if( nums1[ptr1] > nums2[ptr2] )
        {
            nums1[p] = nums1[ptr1];
            ptr1--;
            p--;
        }
        else
        {
             nums1[p] = nums2[ptr2];
            ptr2--;
            p--;
        }
    }
    //如果有一数组提前结束,将还未完成的数组赋值给nums1
    while(ptr2>=0)
    {
        nums1[p] = nums2[ptr2];
        p--; ptr2--;
    }   
     while(ptr1>=0)
    {
        nums1[p] = nums1[ptr1];
        p--; ptr1--;
    }
}

链表

单链表

n个结点链结成一个链表,即为线性表的链式存储结构。

数据结构1 | 单链表其实真的很简单。 - 知乎

结点的组成

​ 结点是由数据域和指针域组成的

数据域:存储数据元素信息的域

指针域:存储后续位置结点的地址

头指针与头结点的异同

头指针:链表中第一个结点的存储位置

头结点:在链表前附设的一个结点

头指针
  • 指向第一个结点的指针,若有头结点,则指向头结点
  • 具有标志作用
  • 无论链表是否为空,头指针不为空。头指针是链表的必要元素
头结点
  • 为了操作的统一和方便而设立,放在第一个元素之前,其数据域一般无意义
  • 头结点方便头插和头删
  • 头结点不一定是链表的必须要素

单链表的定义

typedef int SLDataType;

struct SLNode
{
    struct SLNode* _next;
    SLDataType _data;
};

单链表存储结构与顺序存储结构的优缺点

存储分配方式

  • 顺序存储结构用一段连续存储单元依次存储数据元素
  • 单链表采用链式存储结构,用一组任意的存储单元存放数据元素

时间性能

  • 查找

    • 顺序存储结构 O(1)
    • 单链表 O(n)
  • 插入和删除

    • 顺序存储结构需要移动元素,时间复杂度 O(n)
    • 单链表需要找出位置的指针,插入和删除的时间复杂度为O(1)
  • 空间性能

    • 顺序存储结构需要预分配存储空间,分大了,浪费,分小了,不够用,会造成空间碎片
    • 单链表不需要预分配存储空间

    链表的分类

    单向和双向


在这里插入图片描述


带头和不带头
在这里插入图片描述


循环和非循环

在这里插入图片描述


​ 链表总有八种分类,单向带头循环、单向带头不循环、单向不带头循环、单向不带头不循环;双向带头循环、双向带头不循环、双向不带头循环、双向不带头不循环。

链表的OJ题

203. 移除链表元素 - 力扣(LeetCode)

解题思路:创建两个指针,一个做头,一个指针做尾。遍历需要移除元素的链表,判断不是val的结点链接到新的链表。
在这里插入图片描述

struct ListNode* removeElements(struct ListNode* head, int val) {

    if (head == NULL)
        return head;

    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    struct ListNode* tail = NULL;
    //cur不为空进入循环
    while (cur) {
        //判断头是不是空,直接赋值给新的头
        if (cur->val != val && prev == NULL) {
            tail = prev = cur;
        } else if (cur->val != val && prev != NULL) {
            tail->next = cur;
            tail = cur;
        }
        cur = cur->next;
    }
    //最后的next置空
    if (tail != NULL )
                tail->next = NULL;

    return prev;
}

206. 反转链表 - 力扣(LeetCode)

解题思路:创建三个指针,第一个指针置NULL,第二个指针等于头,第三个指针等于头的next。让第二个指针的next指向第一个指针,再让第二个指针来到第三个指针的位置,第三个指针在往后面走。

在这里插入图片描述

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {

    if(head == NULL)
        return head;

    ListNode* newhead = NULL;
    ListNode* cur = head;
    ListNode* next= NULL;

    while(cur)
    {
        next=cur->next;

        cur->next=newhead;
        newhead=cur;
        cur=next;

    }
    
    return newhead;
}

876. 链表的中间结点 - 力扣(LeetCode)

解题思路:采用快慢指针。快指针走两步,满指针走一步。
在这里插入图片描述

 typedef struct ListNode ListNode;

struct ListNode* middleNode(struct ListNode* head) {
    if(head == NULL)
        return NULL;

    ListNode* slow = head;
    ListNode* tail = head;
    while(tail&&tail->next)
    {
        slow = slow->next;
        tail=tail->next->next;
    }
    return slow;
}
 typedef struct ListNode ListNode;

struct ListNode* middleNode(struct ListNode* head) {
    if(head == NULL)
        return NULL;

    ListNode* slow = head;
    ListNode* tail = head;
    while(tail&&tail->next)
    {
        slow = slow->next;
        tail=tail->next->next;
    }
    return slow;
}
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值