数据结构——c单链表实现

感觉使用头结点的链表不好看,为了让链表结构更好看点,所以就使用指针,让链表指针直接指向链表第一个结点。

在熟悉链表的实现后,栈和队列就更容易实现了,例如

设计链表有如下API

1. void insertToHead(List *l, int val); //在链表头部插入一个结点

2.void insertToTail(List *l, int val); //在链表的尾部插入一个结点

3.int deleteHeadElem(List *l); //删除链表头部的结点

4.void deleteTailElem(List *l); //删除链表尾部的结点

栈的实现:后进先出, 栈指针指向栈顶结点,栈的操作调用链表API( 1, 3)

队列的实现: 先进先出,  队列结点指向链表头和链表尾,队列的操作调用链表API(1, 4)

 

单链表实现如下:

#include<stdlib.h>

typedef struct _list{
    int data;
    struct _list *next;
} LNode, *List;

void initList(List *l)
{
    *l = NULL;
}

// 在头部插入
void insertToHead(List *l, int val)
{
    List temp = (List) malloc(sizeof(LNode));

    if(*l == NULL)
    {    
        temp->data = val;
        temp->next = NULL;
        *l = temp;
            
        return;
    }
    
    temp->data = val;
    temp->next = *l;
    *l = temp;
}

// 在尾部插入
void insertToTail(List *l, int val)
{
    List temp = (List)malloc(sizeof(LNode));

    if(*l == NULL)
    {
        temp->data = val;
        temp->next = NULL;
        *l = temp; //在原指针地址空间写入值

        return;
    }

    List t = *l;
    
    while(t->next != NULL)
    {
        t = t->next;
    }
    
    temp->data = val;
    temp->next = NULL;
    t->next = temp;
}

// 获取链表的长度
int getLength(List l)
{
    if(l == NULL)
        return 0;
    
    int i = 0;
    
    while(l)
    {
        i++;
        l = l->next;
    }
    
    return i;
}

// 返回链表的第K个元素
int getPositionK(List l, int k)
{
    int len = getLength(l);
    if(k < 0 || k > len)
    {
        printf("位置 %d太大,超出链表长度\n",k);
        return -1;
    }
    
    // l直接指向元素,k 为 2 , l->data第一个 l->next->data第二个
    k--;

    while(k != 0 )
    {
        l = l->next;
        k--;
    }
    
    return l->data;
}

// 删除头部元素
void deleteHeadElem(List *l)
{
    if(*l == NULL)
        return;
    
    List t = *l;
    
    // t指向第二个结点
    t = t->next;
    // 释放第一个结点
    printf("销毁结点值为:%d\n", (*l)->data);
    free(*l);
    *l = t;
}

// 删除尾部的元素
void deleteTailElem(List *l)
{
    if(*l == NULL)
        return;

    List t = *l;
    
    // t 指向倒数第二个元素
    while(t->next->next)
        t = t->next;
    
    printf("删除尾部的元素值为:%d\n", t->next->data);
    free(t->next);
    t->next = NULL;  //必须置为NULL,否则出错
}

// 按值查找是否存在
// 0 不存在
// 1 存在
int findValExist(List l, int val)
{
    if(l == NULL)
    {
        printf("链表为空\n");
        return -1;
    }

    while(l)
    {
        if(l->data == val)
            return 1;
        l = l->next;
    }

    return 0;
}

// 链表反转
void reverseList(List *l)
{
    if(*l == NULL)
        return;
    
    List p, q, t;

    p = *l;  // p 指向链表的第一个结点
    q = p->next; // q指向链表的第二个结点
    p->next = NULL; //第一个结点指针域设置为NULL
    
    while(q && q->next)
    {
        t = q->next;
        q->next = p;
        p = q;
        q = t;
    }
    // q指向最后一个元素时停止循环, p指向倒数第二个元素    
    q->next = p;
    *l = q;
}

// 删除链表的倒数第K个结点,必须要改变 指针i里面的值,因为有可能删除头结点
void deletePositionK(List *l, int k)
{
    if(*l == NULL)
        return;

    int len = getLength(*l);
    if( k<0 || k > len)
    {
        printf("k值错误\n");
        return;
    }
    
    List p = *l;
    
    if(len == k)  //删除首结点
    {
        *l = p->next;
        free(p);
        return;
    }

    while(len - k > 1)
    {
        p = p->next;
        k++;
    }
    // p指向倒数第 k+1个结点,也就是被删除结点的前一个结点
    List t = p->next;
    p->next = t->next;
    free(t);
}

void printList(List l)
{    
    while(l)
    {
        printf("%2d",l->data);
        l = l->next;
    }
    printf("\n");
}

运行如下:

$ ./list 
 7 6 5 3 4
链表长度为:5, 第二个元素为:6
消耗结点值为:7
删除尾部的元素值为:4
元素3存在链表中
 6 5 3
反转链表:  3 5 6
删除倒数第3个元素
 5 6
在各个函数中,有的传一级指针有的传二级指针,需要看情况考虑
1. 如果在函数中不改变链表的结构,则使用一级指针
2. 如果在函数中改变链表的结构,则使用二级指针
举个例子,初始化的时候
在main(
  list l; // l是指针
  initList(&l); // &l 是二级指针
)
在initList(list *t)
{
  *t = NULL;
}
我们在main中传递 &l 也就是l的地址,然后函数调用时 *t = NULL 就可以把main函数中的l设置为NULL

 

posted @ 2020-09-18 12:56  桃花春风一杯酒  阅读(183)  评论(0)    收藏  举报