线性结构-线性表

基本概念

线性结构是最常用、最简单的一种数据结构。而线性表是一种典型的线性结构。其基本特点是线性表中的数据元素是有序且是有限的。线性表是一种最简单的线性结构。

特性:

1、必存在唯一的一个“第一元素”、“最后元素”。

2、除第一、最后元素外,均有 唯一的后继、唯一的前驱。

3、线性表是一种相当灵活的数据结构,对线性表的数据元素可以访问、插入和删除。

顺序表

存储结构

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int length;
} SeqList;

两种定义方式

1、将L定义为SeqList类型的变量,如SeqList L

        通过属性访问方式L.data[i-1]访问顺序表中序号为i的元素

2、将L定义为SeqList类型的指针变量,如SeqList * L

        通过指针访问方式L->data[i-1]访问顺序表中序号为i的元素

基本操作

构造一个线性表L。

void createList(SeqList *list, int n) 
{
    int i;
    for (i = 0; i < n; i++) 
    {
        scanf("%d", &list->data[i]);
    }
    list->length = n;
}

判断是否为空ListEmpty( L )

若L为空表,则返回TRUE,否则FALSE。

bool ListEmpty(SeqList L)
{
	return L.length==0;	
 } 

查找

按位置查找GetElem( L, i, &e)

用e返回L中第i个元素的值。

int GetElem(SeqList L,int i,int* e) 
{
	if(L.length==0||i<1||L.length
按元素查找LocateElem( L, e)

返回元素序号

int LocateElem(SeqList list,int e)
{
	int i = 0;
	while( (i<=list.length) && (list.data[i]!=e) )
	{
		i++;
	}
	if(i<=list.length)
	{
		printf("%d",i+1);	
		return i+1; //返回序号
	}
	
	else
    {
		printf("未找到");
		return -1;
	}
	
} 

PutElem( &L, i, &e )1≤i≤LengthList(L),L 中第 i 个元素赋值e

void PutElem(SeqList *L, int i, int *e )
{
	if((i<1) || (i>L->length))
	{
		printf("位置不在范围内\n");
		return;
	}
	else
		L->data[i-1]=*e;
}

插入元素

ListInsert( &L, i, e )1≤i≤LengthList(L)+1,在 L 的第 i 个元素之前插入新的元素 e,L 的长度增1。

void insertElement(SeqList *list, int position, int element) {
    if (position < 1 || position > list->length + 1) {
        //printf("插入位置不在范围内\n");
        return;
    }
    
    int i;
    for (i = list->length - 1; i >= position - 1; i--) {
        list->data[i+1] = list->data[i];
    }
    
    list->data[position-1] = element;
    list->length++;
}

删除元素

ListDelete(&L, i, &e)  1≤i≤LengthList(L) ,删除 L 的第 i 个元素,并用 e 返回其值,L 的长度减1。

void DelElement(SeqList*list,int position,int *element) 
{
	if (position < 1 || position > list->length + 1 ) {
        //printf("删除位置不在范围内\n");
        return;
    }
    int i;
    *e=list->data[position-1]
    for (i = position; i < list->length; i++) {
        list->data[i-1] = list->data[i];
    }
    
    list->length--;
}

输出线性表内容

void printList(SeqList *list) {
    int i;
    for (i = 0; i < list->length; i++) 
    {
        printf("%d ", list->data[i]);
    }
    printf("\n");
}

顺序存储结构优缺点

优点:无需为表示表中元素之间的逻辑关系而增加额外的存储空间。

        可以快速的存取表中任一位置的元素。

缺点:插入和删除操作需要移动大量元素。

        当线性表长度变化较大时难以确定存储空间的容量。

        造成存储空间的“碎片”。

单链表

链式存储结构分为单链表,循环链表,双向链表和静态链表

存储结构

typedef struct Node
{
    //链表中的结点包括数据域和指针域两个域。
    int data;    //数据域用来存储节点的值,
    struct Node* next;    //指针域用来存储节点本身的信息连接关系,由后继指针指示其位置。
} Node,*LinkList;

LinkList L;//*L为单链表的头指针

LinkList和Node*同为结构指针类型

常用LinkList类型定义指针变量,常用Node*定义单链表中的结点类型

 为什么要定义Node型指针?

定义了一个结构体Node,其中包含一个int类型的data成员和一个指向Node结构体类型的指针next成员。在定义链表时,我们通常使用指向Node结构体类型的指针来表示节点之间的连接关系。因此,将next定义为struct Node*类型是合适的。

如果将next定义为int类型,那么在表示链表节点之间的连接关系时会变得困难,因为int类型只能指向整数类型的数据,无法表示节点的连接关系。因此,为了正确表示链表结构,应该将next定义为指向Node结构体类型的指针。

基本操作

建立单链表

建立空表
void InitList(LinkList *L) 
{
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
}

为什么L前要加*号?

在函数参数中,如果需要修改指针本身的值(即改变指针指向的地址),需要传递指针的指针。在这种情况下,需要使用一个额外的指针来存储指针的地址,因此需要在参数前加上一个星号。在这里,InitList函数需要初始化一个链表指针L,因此需要传递指向指针的指针。

头插法建表
LinkList CreateList(LinkList *L, int data[], int n) 
{
    Node* p = *L;
    for (int i = 0; i < n; i++) {
        Node* q = (LinkList)malloc(sizeof(Node));
        q->elem = data[i];
        q->next = p->next;
        p->next = q;
    }
    
    return *L;
}
尾插法建表
LinkList CreateList(LinkList *L, int elem[], int n) 
{
    Node* p = *L;
    for (int i = 0; i < n; i++) {
        Node* q = (LinkList)malloc(sizeof(Node));
        q->data = elem[i];
        q->next = p->next;
        p->next = q;
        p = q;
    }
    
    return *L;
}

带头结点单链表长度

int ListLength(LinkList L)
{
	Node *p = L->next;
	int j = 0;
	while(p!=NULL)
	{
		p=p->next;
		j++;
	}
	return j;
}

顺链头开始,计数器j初值为0

当前指针p指向链表的首元结点,p=L->next

p依次往后(j++)直到表尾(p==NULL)

查找

按序号查找
ElemType   Get_Elem(Linklist *L , int  i)
{
    int j ;   Node *p;
    p=L->next;  j=1;      //使p指向第一个结点      
    while  (p!=NULL && jnext;  
        j++;  
    }    //移动指针p , j计数  
   	if  (j!=i)  
        return(-32768) ;
    else      
        return(p->data);//   p为NULL 表示i太大;  j>i表示i为0  
}
按值查找
LNode *Locate_Node(LinkList *L,int key)
{
   Node *p=L–>next;
   while  ( p!=NULL&& p–>data!=key)    
       p=p–>next;     
       if  (p–>data==key)   
            return p;
       else  
       {    
            printf(“所要查找的结点不存在!!\n”); 
            retutn(NULL);        
        }
}

插入

bool ListInsert_L(LinkList *L, int i, ElemType e)
{
	Node *p=L;;j=0;
	while(p&&jnext;
		j++
	}
    if (!p || j > i-1)
      return -1;
	Node * q=(LinkList)malloc(sizeof(Node));
	q->next=p->next;
	q->data=e;
	p->next=q;
	return 1;
}

删除

void DelList(LinkList *L,int n)
{
	Node*p=(*L)->next;
	while(p!=NULL&&p->next!=NULL)
	{
		if(p->data==p->next->data)
		{
			Node*temp=p->next;
			//创建一个临时指针temp,
			//指向当前节点p的下一个节点,即要删除的重复节点。
			p->next=temp->next;
			//将当前节点p的next指针指向要删除节点temp的下一个节点,
			//相当于跳过了要删除的节点。
			free(temp);
			//释放掉要删除的节点temp的内存空间,防止内存泄漏。
		}
		else
			p=p->next;//注意不要漏掉这行代码 
	}
}

输出

void PrintList(LinkList L)
{ 
	Node *current = L->next; 
	while(current != NULL) 
	{ 
		printf("%d ", current->data);
		current = current->next; 
	} 
	printf("\n"); 
}

 单链表清空

首先将链表的第一个节点保存在指针p中,然后将链表的头节点指向p的下一个节点,即删除了p节点。最后释放p节点的内存空间,完成了一个节点的删除操作。循环执行这个过程直到链表为空,即所有节点都被删除,链表被清空。

void ClearList(LinkList*L)
{
	while ((*L)->next != NULL)
    {
        Node *p= (*L)->next;
        (*L)->next=p->next;
        free(p);
    }
}

参考文献

数据结构_中国大学MOOC(慕课) (icourse163.org)

你可能感兴趣的:(#数据结构,c#,数据结构,算法)