数据结构(借鉴408)-线性表

数据结构

  1. 逻辑结构、物理结构
  2. 时间复杂度、空间复杂度

线性表

顺序表

#define MAX_SIZE 100
typedef int ElemType;
typedef struct seqlist{
ElemType data[MAX_SIZE];
int length;
}SeqList
//初始化
SeqList* L->data = (SeqList*)malloc(sizeof(Elemtype)*MAX_SIZE);
//查找
int FindByValue(SeqList* L, int e){
int i;
for(i=0;i<L->length;i++){
if(L->data[i] == e)
return i+1;
}
return -1;
}
//插入
bool ListInsert(SeqList*L, int i, int e){
if(i<1 || i>L->length+1) return false;
if(L->length >= MAX_SIZE) return false;
for(int j = L->length;j>=i;j--){
L->data[j] = L->data[j-1];
}
L->data[i-1] = e;
L->length++;
return true;
}
//删除
bool ListDelete(SeqList*L, int i, int* e){
if(L->length == 0) return false;
if(i<1 || i>L->length) return false;
e = L->data[i-1];
for(int j=i;j<L->length;j++){
L->data[j-1] = L->data[j];
}
L->length--;
return true;
}

1.编写算法,将两个非递减有序顺序表A和B合并成一个新的非递减有序顺序表C,已知顺序表A和B的元素个数分别为m和n。其中顺序表采用动态分配空间,定义如下。

Typedef struct{
Elemtype * elem; //存储空间基地址
int length; //当前长度
int listsize; //当前分配的存储容量
}SeqList;

算法思想:【双指针不回溯】
要合并两个有序的顺序表A和B,那么需要开辟一个新的数组空间C,当A和B都未到末尾时,挨个比较A和B的元素,将小元素放入到C中,当元素相等时,只保留一个。当A或B结束时,将未结束表的元素逐个放入到C中。

void CombinationSeqList &L1SeqList &L2SeqList &L3){
int i=0,j=0,k=0;
while((i!=L1->length)&&(j!=L2->length){
if(L1->data[i]>L2->data[j]){
L3->data[k++]=L2->data[j++];//L1中元素大,合并L2中元素到L3
}
else if(L1->data[i]==L2->data[j]){
L3->data[k++]=L2->data[j++];//元素相等,只保留一个,合并到L3
i++;
}
else{
L3->data[k++]=L1->data[i++];//L2中元素大,合并L1中元素到L3
}
L3->length++;
}
while(i<L1->length){ //L2结束,合并L1中的元素到L3
L3->data[k++]=L1->data[i++];
L3->length++;
}
while(j<L2->length){ //L1结束,合并L2中的元素到L3
L3->data[k++]=L2->data[j++];
L3->length++;
}
}

2.试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表(a1,a2,a3,...,an-1,an)->(an,an-1,...,a3,a2,a1)。

算法思想:【逆置】要实现数组的逆置,且要求使用原有空间,只需要将对应的数组元素位置交换就可以,如第1个(对应下标是0)和第n个(对应下标是n-1)交换,第2个(对应下标是1)和第n-1个(对应下标是n-2)交换,需要交换的连个元素的下标之和是n-1,其中n是表长。

int ListOppose(SeqList &L){
int i;
ElemType x;
//只需要遍历原表的一半就可以实现数据元素位置的交换
for(i=0; i<L->length/2; i++){
x = L->data[i];
L->data[i] = L->data[L->length-i-1]; //数据元素交换 -> 逆置
L->data[L->length-i-1]=x;
}
return 1
}

3.一个线性表中的元素全部为正或负整数,试设计一算法,再尽可能少的时间内重排该表,将正、负整数分开,使线性表中所有负整数在正整数前面。

算法思想:【快速排序】
算法中提出暂存的第一个元素不作为比较的标准,比较的标准是0,判断对应的元素是否是正数或者负数。

int ReArrange(SeqList &a, int n){
int low=0,high=n-1;
int t=a[low]; //枢轴元素,只是暂存,不作为比较对象
while(low<high){
while(low<high&&a[high]>=0) //寻找小于0的元素
high--;
a[low]=a[high];
while(low<high&&a[low]<0) //寻找大于0的元素
low++;
a[high]=a[low];
}
a[low]=t; //将枢轴元素放到最终位置
return 1
}

4.已知线性表(a1,a2,a3,...,an-1,an)按顺序结构存储且每个元素为不相等的整数。设计把所有奇数移到所有偶数前边的算法(要求时间最少,辅助空间最少)

算法思想:【快排】
通过对数字模2是否等于0进行判断,使得奇数元素都在左边,偶数元素都在右边。
具体方法:设置两个指针变量分别指向表头和表尾,它们分别向中间移动,指向表头的数组指针如果遇到偶数且指向标为的数组指针遇到奇数,则互相交换,然后继续移动直至两者相遇。

void part(int arry[], int n){
int i,j.temp;
i=0,j=n-1;
while(i<j){
while(i<j&&array[i]%2 != 0) //i指针从前往后扫描数组,遇到偶数停下
i++;
while(i<j&&array[j]%2 == 0) //j指针从后往前扫描数组,遇到奇数停下
j--;
if(i<j){ //如果i<j,交换两个指针分别指向的元素
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}

5.已知在一维数组A[1,...,m+n+1]中依次存放着连个顺序表:(a1,a2,a3,...,am)和(b1,b2,b3,...,an)。试编写程序,将数组中凉饿顺序表的位置互换,即将(b1,b2,b3,...,an)放在(a1,a2,a3,...,am)的前面。

算法思想:
第一步:将A中所有元素逆置,变成bn,bn-1,...,b1,am,am-1,...,a1。
第二步:将前n个逆置
第三步:将后m个逆置

void Reserve(ElemType A[], int left, int right, int arraySize){
if(left>=right || right>=arraySize)
return;
int mid =(left+right)/2;
for(int i=0; i<=mid-left; i++){
ElemType temp = A[left+i];
A[left+i] = A[right-i];
A[right-i] = temp;
}
}
void Exchange(ElemType A[], int m, int n, int arraySize){
Reserve(A,0,m+n-1,arraySize);
Reserve(A,0,n-1,arraySize);
Reserve(A,n,m+n-1,arraySize);
}

6.设有一个线性表存放在一维数组a[0,...,n-1]中,编程将数组中每个元素循环右移k位,要求只用一个辅助单元,时间复杂度为O(n)。

void Reserve(int* array, int p, int q){
for(; p<q; p++,q--){
int temp = array[q];
array[q] = array[p];
array[p] = temp;
}
}
void RightShift(int* array, int n, int k){
k%=n;
Reserve(array,0,n-1);
Reserve(array,0,n-1-k);
Reserve(array,n-k,n-1);
}

单链表

typedef struct LNode{
ElemType data;
struct LNode* next;
}LNode, *LinkList;
//结点创建
LNode *p;
p=(LNode*) malloc(sizeof(LNode));
p->data = 20;
p->next = NULL;
//添加q
q->next = p->next;
p->next = q;
//删除p的直接后继
q=p->next;
p-next =q->next;
free(q);
  • 头插法 单链表逆序 头指针(不是数据结点)

  • 尾插法 单链表顺序 尾指针(数据结点)

    //头插法

    LinkList Creat_list(LinkList head){
    head = (LinkList)malloc(sizeof(LNode)); //为头指针开辟内存空间
    LNode *node = NULL; //定义工作指针
    head->next = NULL;
    node = head->next; //将最后一个结点的指针域永远保持为NULL

    int count=0; //创建结点的个数
    scanf("%d", &count);
    for(int i=0; i<count; i++){
    node = (LNode*)malloc(sizeof(LNode)); //为新结点开辟内存空间
    node->data = i; //为新结点的数据域赋值
    node->next = head->next;//将头指针所指向的下一个结点的地址,赋值个新创建的结点的next
    head->next = node; //将新创建的结点的地址赋值个头指针的下一个结点
    }
    return head;

    }

    //尾插法
    LinkList Creat_list(LinkList head){
    head = (LinkList)malloc(sizeof(LNode)); //为头指针开辟内存空间
    LNode *node = NULL; //定义工作指针
    LNode *end = NULL; //定义尾指针
    head->next = NULL; //初始化头结点知道的下一个地址为NULL
    end = head; //未创建其余结点之前,只有一个头结点

    int count=0; //创建结点的个数
    scanf("%d", &count);
    for(int i=0; i<count; i++){
    node = (LNode*)malloc(sizeof(LNode)); //为新结点开辟内存空间
    node->data = i; //为新结点的数据域赋值
    end->next = node; //新结点赋值给end的下一个
    end = node; //新结点成为新的尾结点
    }
    end->next = NULL;
    return head;

    }

    //查找
    bool GetElem_L(LinkList &L, int i, ElemType &e){
    //L为带头结点的单链表的头指针
    //当第i个元素存在时,其值赋值给e并返回true,否则返回false
    LNode *p = L->next;
    int j = 1;
    while(p && j<i){
    p = p->next;
    ++j;
    }
    if(!p || j>i) return false;
    e = p->data;
    return true;
    }

    LNode Locate_Node(LNode L, int key){
    /
    在以L为头结点的单链表中查找值为key的第一个结点
    /
    LNode * p = L->next; //p指向第一个数据结点
    while(p!=NULL && p->data != key)
    p = p->next;

    if(p->data == data)
    return p;
    else{
    printf("No Result.\n");
    return NULL;
    }

    }

    //插入
    bool ListInsert_L(LinkList &L, int i, ElemType e){
    //在带头结点的单链线性表L中第i个位置之前插入元素e
    LNode *p = L;
    int j=0;
    while(p && j<i-1){ //寻找第i-1个结点的位置的过程
    p = p->next;
    ++j;
    } //寻找第i-1个结点
    if(!p ||j>i-1) return false;

    //执行插入操作
    LNode *s;
    s=(LinkList)malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;

    }

    //删除
    bool ListDelete_L(LinkList &L, int i, ElemType &e){
    //在带头结点的单链表L中,删除第i个元素,并由e返回其值
    LNode *p;
    LNode *q;
    p=L;
    int j=0;
    while(p->next && j<i-1){ // 寻找第i个结点,并令p指向其前驱
    p = p->next;
    ++j;
    }
    if(!(p->next) || j>i-1) return false; //删除位置不合理
    q=p->next;
    p->next = q->next; //删除并释放结点
    e=q->data;
    free(q);
    return true;
    }

    void Delete_LinkList(LNode *L, int key){
    //删除以L为头结点的单链表中值为key的第一个结点
    LNode *p = L, *q=L->next;
    while(q!=NULL && q->data!=key){
    p=q;
    q=q->next;
    }
    if(q->data == key){
    p->next = q->next;
    free(q);
    }
    else printf("NULL\n");
    }

    //从头到尾依次遍历,输出各个元素的值
    void printL(LinkList L){
    LNode *p;
    p=L->next;
    while(p!=NULL){
    printf("%d ",p->data);
    p=p->next;
    }
    }

1.给定键值按升序排列的带头结点的单链表La和Lb,将其合并成升序排列的单链表,并返回新链表的表头指针。要求利用原表的结点数据空间。

算法分析:若La、Lb两个链表的长度分别为m,n,则合并链表的时间复杂度为O(m+n)。

LNode *Merge_LinkList(LNode *La, LNode *Lb){
//合并以La和Lb为头结点的两个有序链表
LNode *Lc,*pa,*pb,*pc,*ptr;
Lc=La;
pc=La;
pa=La->next;
pc=Lb->next;
whlile(pa!=NULL && pb!=NULL){
if(pa->data < pb->data){ //将pa所指的结点合并,pa指向下一个结点
pc->next = pa;
pc=pa;
pa=pa->next;
}
if(pa->data > pb->data){ //将pb所指的结点合并,pb指向下一个结点
pc->next = pb;
pc=pb;
pb=pb->next;
}
if(pa->data == pb->data){ //将pb所指的结点合并,pb所指结点删除
pc->next = pa;
pc=pa;
pa=pa->next;
ptr=pb;
pb=pb->next;
free(ptr);
}
if(pa!=NULL) //将剩余结点链接上
pc->next = pa;
else
pc->next = pb;
free(Lb);
return Lc;
}
}

2.删除单链表中所有值重复的结点,使得所有结点的值都不相同

基本思想:从单链表的第一个结点开始,对每个结点进行检查,检查链表中的结点的所有后继结点,只要有结点的值和该结点的值相同,就删除;然后检查下一个结点,直到所有的结点都检查过。

void Delte_Node_vale(LNode *L){ //时间复杂度O(n2) 空间复杂度O(1)
//删除以L为头结点的单链表中所有值相同的结点
LNode *p=L->next, *q, *ptr; //q是被删除结点的直接前驱 ptr是被删除元素
while(p!=NULL){
q=p;
ptr=p->next;
//检查结点p的所有后继结点ptr
while(ptr!=NULL){
if(ptr->data == p->data){
q->next = ptr->next;
free(ptr);
ptr=q->next;
}
else{
q=ptr;
ptr=ptr->next;
}
}
p=p->next;
}
}
//其他方法:借助Hash,用空间换时间

3.试写出逆转线性单链表的算法,单链表结点的类型定义如下

typedef struct node{
ElemType data;
struct node* next;
}LNode,*LinkList;

基本思想:采用头插法,以此借助原有结点的存储空间建立新的单链表

LNode *revert_list(LNode* head){
if(NULL == head)
return head;
LNode *p = head->next;
head->next = NULL;
LNode *tmp = NULL;
while(p){
tmp = p->next;
p->next = head->next; //头插法
head->next = p;
p=tmp;
}
return head;
}
posted @   Theseus‘Ship  阅读(80)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
Live2D
欢迎阅读『数据结构(借鉴408)-线性表』
点击右上角即可分享
微信分享提示