顺序表是数据结构的一种,它可以存储整型等内置类型和自定义类型
通讯录就是用顺序表的自定义类型实现的
顺序表经典算法题:
移除元素
解题思路:

//这里的函数返回值是指返回不为val的值的个数
//numSize是指数组元素的个数
int removeElement(int* nums, int numsSize, int val) {
int src = 0;
int dst = 0;
//int src, dst;
//src = dst = 0;
while(src < numsSize)
//src不能等于numsSize:src是下标,numsSize 是有效数据个数,二者相差1
{
if (nums[src] == val)//记得 == 才是相等
{
src++;
}
else
{
nums[dst] = nums[src];
src++;
dst++;
}
}
return dst;
}
解题思路:

代码如下:
//nums1Size是指nums1的数组长度
//nums2Size是指nums2的数组长度
//上面这两个参数用不上
//这里的函数的返回值为空,原因是新的数组就在nums1中形成
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
int l1 = m - 1;
int l2 = n - 1;
int l3 = m + n - 1;
while(l1 >= 0 && l2 >= 0)//只有两个同时大于等于0 才能 够说明这两个数组中的11 和 12 是有效的,才可以比较,只要有一个为假,就不能比较,要跳出循环
{
if (nums1[l1] >= nums2[l2])
{
nums1[l3--] =nums1[l1--];
}
else{
nums1[l3--] =nums2[l2--];
}
}
//出了循环有两种情况:l1 >= 0 或者 l2 >= 0
//只需要关注l2 >= 0 的情况;l1 >= 0 时nums1就已经时有序数组了
while(l2 >= 0)
{
nums1[l3--] =nums2[l2--];
}
//此时nums1中包含了nums2中的数据,并且num1是升序数组
}
//注意nums1中对应的是l1和l3,nums2对应的是l2;写代码时不要将对应的位置搞混了
针对顺序表:在中间/头部插入数据导致效率低下、增容降低运行效率、增容造成的空间浪费(100个空间不够用,数据的有效字节数为105,二倍申请到200个字节,浪费了95个空间)
解决办法:链表
链表在物理结构上不是线性的;
在逻辑结构上一定是线性的,例如:在堆上的存储看起来是混乱的,但它是用节点中的地址链接起来,将他拉成一条直线就是线性的
链表也是线性表的一种;线性表在物理结构上不一定是线性的
例:
int a = 0;
float b = 0;
//在内存中存储a和b的空间不一定是连续的(线性的)
在逻辑结构上一定是线性的
链表是是由一个一个(节点)结点组成,就像火车是由一节一节车厢组成的

链表的好处:可以任意删除一个节点,再将其它节点进行连接起来,节点是相互独立的;数组在删除时不能直接将数组中某个元素删除
节点是有什么组成的呢?
节点是由 数据 和 指向下一个节点的指针 构成的
那我们现在如何定义链表呢? 定义链表就是定义节点的结构,可以使用结构体完成对链表的定义
struct SListNode//S 是指singl - 单链表 Node - 节点
{
int data;//数据
struct SListNode * next;//指向下一个节点的指针,它的数据类型还是结构体指针
}
以下是链表的代码:
代码对应的思路在这里
SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义节点的结构
//数据 + 指向下一个节点的指针
typedef int SLDataType;
typedef struct SLTList
{
SLDataType data;
struct SLTNode* next;//
//SLT* next;//注意这里的结构体中的成员不得使用重命名后的名字
}SLT;
//链表打印
void SLTPrint(SLT* phead);
SLT* SLTBuyNode(SLDataType x);
//尾插
void SLTPushBack(SLT** pphead, SLDataType x);//不需要返回值
//头插
void SLTPushFrant(SLT** pphead, SLDataType x);
//尾删
void SLTPopBack(SLT** pphead);
SList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void SLTPrint(SLT* phead)
{
SLT* pcur = phead;
while (pcur)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("NULL\n");
}
SLT* SLTBuyNode(SLDataType x)
{
SLT* newnode = (SLT*)malloc(sizeof(SLT));
if (newnode == NULL)//易错点
{
perror("SLTBuynode fail");
exit(1);//记得带上() --- return 1;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLT** pphead, SLDataType x)
{
assert(pphead);//在使用*pphead 时不能对空指针进行解引用
//*pphead 可以为空,代表着没有节点,时空链表
SLT* newnode = SLTBuyNode(x);//这里已经初始化过了不需要加上类型
//空链表
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLT* pcur = *pphead;
while (pcur->next)//是它的下一个地址为空才能找出尾节点
{
pcur= pcur->next;
}
pcur->next = newnode;
}
}
//头插的空链表和非空链表是通用的
void SLTPushFrant(SLT** pphead, SLDataType x)
{
assert(pphead);
SLT* newnode = SLTBuyNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
//尾删
void SLTPopBack(SLT** pphead)
{
assert(pphead && *pphead);
//pphead - 表示传参时不能传空指针,*pphead无法解引用
//不能是空链表,*pphead为空时,代表该链表是空链表
//只有一个节点时
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//多个节点时
else
{
//先找未节点和前一个节点,将未节点进行释放,前一个节点中指向下一个节点的地址置为空
SLT* prve = *pphead;
SLT* ptail = *pphead;
while (ptail->next)
{
prve = ptail;//这里是倒数第二次循环时找到尾节点中的前一个节点
ptail = ptail->next;//这里是尾节点
}
free(ptail);
ptail = NULL;
prve->next = NULL;
}
}
Test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void SLTest01()
{
//创建节点 - 这里是为了测试打印节点用的,在正规中我们使用插入的形式进行创建节点
SLT* node1 = (SLT*)malloc(sizeof(SLT));
node1->data = 1;
SLT* node2 = (SLT*)malloc(sizeof(SLT));//使用
node2->data = 2;
SLT* node3 = (SLT*)malloc(sizeof(SLT));
node3->data = 3;
SLT* node4= (SLT*)malloc(sizeof(SLT));
node4->data =4;
//连接节点
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
//创建一个新的节点进行打印, 为啥要创建新的节点:为了后续要再使用node1 时方便回到原来的位置
SLT* plist = node1;
SLTPrint(plist);
}
void SLTest02()
{
SLT* plist = NULL;
SLTPushBack(&plist, 5);//别忘了取地址
SLTPrint(plist);
SLTPushBack(&plist, 6);
SLTPrint(plist);
SLTPushBack(&plist, 7);
SLTPrint(plist);
//假设前面的是非空链表
SLTPushBack(&plist, 8);
SLTPrint(plist);
//SLTPushBack(NULL, 8);//err
//SLTPrint(plist);
SLTPushFrant(&plist, 1);
SLTPrint(plist);
SLTPushFrant(&plist, 2);
SLTPrint(plist);
SLTPushFrant(&plist, 3);
SLTPrint(plist);
SLTPushFrant(&plist, 4);
SLTPrint(plist);
////SLTPushFrant(NULL, 1);err
////SLTPrint(plist);
//测试尾删
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
// SLTPopBack(&plist);//err -测试删除空链表
// SLTPrint(plist);
}
//上面的02既可以检测空链表也可以检测非空链表
int main()
{
SLTest01();
SLTest02();
return 0;
}
单链表经典算法题:
移除链表元素

被折叠的 条评论
为什么被折叠?



