数据结构1-单链表

链表与数组

在编程语言中,数组数据结构(array data structure),简称数组(Array),是一种数据结构,是数据元素(elements)的集合。有限个相同类型的元素按顺序存储,用一个名字命名,然后用编号区分他们的变量的集合;这个名字称为数组名,编号称为下标。

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不同与数组的是,并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储。

一、链表与数组的优缺点

// 数组的缺点:
//
//    1.一旦数组定义,则大小固定,无法进行修改(数组的大小)。
//    2.数组插入和删除的效率太低,时间复杂度O(n)。
//    
// 数组的优点:
//    
//    1.下标访问,速度快,时间复杂度是O(1)
//
//////////////////////////////////////////////////////////
//
// 链表:
//   
// 链表的优点:
//   
//    1.资源允许的情况下,规模可以不断的增长或减小。
//    2.删除和添加效率高,O(1)
//
// 链表的缺点:
//
//    链表的遍历过程效率比较低。

二、数据结构类型的定义

//方法1
int array1[10] = {0};    

//方法2
int array2[] = {12, 23, 34, 45, 56, 67, 78};  

这里,我们的array1和array2都在定义时直接分配了 <栈> 上的内存空间


//方法3
int *array3 = NULL;
array3 = (int *)malloc(sizeof(int) * 100);   
if(array3 != NULL){
     fprintf(stderr, "the memory is full!\n");
     exit(1);
}

array3是一个int类型的指针,指向我们在 <堆> 上用malloc分配的4bytes ×100 = 400 bytes大小的空间。

我们的数组虽然在O(1)的时间复杂度访问下标进行数据存取查找,可是一般数组定义后,大小不能够进行动态变化,且插入删除效率过低,时间复杂度为O(n).
所以,为了弥补这些不足,我们又学习到了链表。

链表的定义
链表是一种物理存储单元上非连续、非顺序的存储结构,链表由相同结构的链表节点构成,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。可以关注链表节点的结构:

typedef struct List_node
{
    int                data;
    struct List_node  *next;
}List_node;

如何把两个节点进行连接:
这个指针指向类型为链表节点,这样每一个节点就可以记录下下一个节点的位置,从而把原本毫不相干的链表节点串接起来。

单链表图

一个链表节点定义;

 typedef struct P_Node{     
 //数据域
        char name[20];  //姓名
        char sex;       //m 男 w 女 性别
        char age;       //年龄
        char jobs[20];  //职业
 //指针域
        struct P_Node *next;    
 }P_Node; 
//一个职工的结构体

再申请一个链表节点;

P_Node node1   = {0};
P_Node *p_node = (Node *)malloc(sizeof(Node));
node1.next     = p_node;

三、链表的常见接口

我们把可以对链表的操作是在.h文件中进行声明的,我们叫这样的声明为接口,如下是单链表的接口声明:

/*list.h
 *
 * 带头节点的链表
 *
 *
 */

#ifndef _LIST_H
#define _LIST_H

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#endif



#define TRUE  (1)
#define FALSE (0)

typedef unsigned char Boolean;


//定义链表节点类型

typedef struct List_node{
    int                data;
    struct List_node  *next;
}List_node;

typedef List_node  *List_head;


/*
 *链表的接口
 *---------------------------------------------------------
 * 常见接口:
 *      1.初始化
 *      2.销毁
 *      3.增加
 *      4.删除
 *      5.查找
 *      6.修改
 *      7.排序
 *      8.显示
 *----------------------------------------------------------
 */

List_head   init_list(void)                                          ;
void        destroy_list(List_head *head)                           ;
Boolean     push_front(List_head head, int value)                    ;
Boolean     push_back(List_head head, int value)                     ;
Boolean     pop_front(List_head head)                                ;
Boolean     pop_back(List_head head)                                 ;
Boolean     find_node(List_head head, int value, List_node **node)   ;
void        modify_node(List_node * node, int value)                 ;
Boolean     insert_node(List_head head, int index, int value)        ;
void        sort_list_ascend(List_head head)                         ;
void        sort_list_descend(List_head head)                        ;
void        show_list(List_head head)                                ;
int         get_list_length(List_head head)                          ;

我们可以通过定义 包裹函数 来缩短程序。每个包裹函数完成实际的函数调用,检查返回值,并在发生错误时终止进程。
包裹函数其实就是封装函数,调用一个函数来实现这个功能。既然我们会经常用到一些函数并且还要进行错误处理,那么,我们可以将这些函数封装起来,也放入头文件.h当中。

//包裹函数
#ifndef _TOOL_H_
#define _TOOL_H_

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#endif

static List_node *create_node(void);
static void *Malloc(size_t size);
static void swap(void *a, void *b, int length);

static List_node *create_node(void)
{
    //申请节点,并且对节点初始化(置为0)
    List_node *node = (List_node *)Malloc(sizeof(List_node));           
    bzero(node, sizeof(List_node));
    return node;
}

static void *Malloc(size_t size)
{
    void *result = malloc(size);
    if(result == NULL){   //失败情况处理
        fprintf(stderr, "the memory is full!\n");
        exit(1);
    }
    return result;
}

static void swap(void *a, void *b, int length)
{
    void *temp = Malloc(length);

    memcpy(temp, a, length);
    memcpy(a, b, length);
    memcpy(b, temp, length);

    free(temp);
}

四、链表接口的实现

为了能够使用这些我们所提供的接口,我们要能够实现这些操作,可以在list.c中进行实现。


//1初始化   List_head  <==>List_node *
List_head   init_list(void) 
{
    List_head head = NULL;
    head = create_node();

    return head;
}

//2 销毁
void        destroy_list(List_head *head)
{
    List_node *p = NULL;
    List_node *q = NULL;

    if(head == NULL || *head == NULL){
        return ;
    }

    p = *head;
    //从头到尾一次销毁知道最后一个节点
    while(p != NULL){
        q = p;
        p = p->next;
        free(q);
    }
    //防止野指针
    *head = NULL;
}

//3 头部插入
Boolean     push_front(List_head head, int value)
{
    List_node *node = NULL;
    if(head == NULL){
        return FALSE;
    }

    node = head->next;
    head->next = create_node();
    head->next->data = value;
    head->next->next = node;
    /*
     node = create_node();
     node->data = value;
     node->next = head->next;
     head->next = node;
     */

    head->data++;
    return TRUE;

}

//尾部插入
Boolean     push_back(List_head head, int value)
{
    List_node *node = NULL;
    if(head == NULL){
        return FALSE;
    }

    node = head;
    //寻找尾节点
    while(node->next != NULL){
        node = node->next;
    }
    //插入新节点
    node->next  = create_node();
    node->next->data = value;

    head->data++;
    return TRUE;

}

//头部删除节点
Boolean     pop_front(List_head head)
{
    List_node * p = NULL;
    //判断链表是否存在或者是否有有效节点
    if(head == NULL ||head->next == NULL){
        return FALSE;
    }

    p = head->next;
    head->next = p->next;
    free(p);

    head->data--;
    return TRUE;

}

//尾部删除节点
Boolean     pop_back(List_head head)
{ 
    List_node *p = NULL;
    //判断链表是否存在或者是否有有效节点
    if(head == NULL ||head->next == NULL){
        return FALSE;
    }

    p = head;
    //找到倒数第二个结点
    while(p->next->next != NULL){
        p = p->next;
    }

    free(p->next);
    p->next = NULL;

    head->data--;
    return  TRUE;
}

//寻找指定节点
Boolean     find_node(List_head head, int value, List_node **node)
{
    List_node *p = NULL;

    if(head == NULL){
        return FALSE;
    }

    p = head->next;
    while(p != NULL){
        //寻找匹配的节点
        if(p->data == value){
            //找到而且*node节点不等于NULL,将p存入*node
            if(*node != NULL){
                *node = p;
            }//end if
            return TRUE;
        }//end if
        p = p->next;
    }
    return FALSE;
}

//修改某节点
void        modify_node(List_node * node, int value)
{
    node->data = value;
}

//在指定节点后插入一个节点
Boolean     insert_node(List_head head, int index, int value)
{
    List_node *node   = NULL;
    List_node *p_node = NULL;
    int  cout = index;

    //链表不存在或者插入不合法
    if(head == NULL || index < 0 || index > head->data){
        return FALSE;
    }

    //寻合适位置,被插入节点的前一个位置
    node = head;
    while(cout--){
        node = node->next;
    }
    //p_node记录下要插入节点的下一个位置
    p_node = node->next;
    //创建新节点并插入
    node->next = create_node();
    node->next->data = value;
    node->next->next = p_node;

    head->data++;
    return TRUE;

}
//升序排序
void        sort_list_ascend(List_head head)
{
    if(head == NULL || head->data < 2){
        return ;
    }

    List_node *p = NULL;
    List_node *q = NULL;

    /
    p = head->next;
    while(p->next != NULL){
        q = p->next;
        while(q != NULL){
            if(p->data > q->data){
                swap(p,q,(unsigned long)(&((List_node *)0)->next));
            }
            q = q->next; 
        }
        p =p->next;
    }
    
/*
    for(p = head->next; p->next; p = p->next){
         for(q = p->next; q; q = q->next){
                if(p->data > q->data){
                    swap(p,q,(unsigned long)(&((List_node *)0)->next));
                }
     }*/ 
}

//降序
void        sort_list_descend(List_head head)
{
    if(head == NULL || head->data < 2){
        return ;
    }

    List_node *p = NULL;
    List_node *q = NULL;

    /
    p = head->next;
    while(p->next != NULL){
        q = p->next;
        while(q != NULL){
            if(p->data < q->data){
                swap(p,q,(unsigned long) (&((List_node *)0)->next));
            }
            q = q->next; 
        }
        p =p->next;
    }
    

    /* for(p = head->next; p->next; p = p->next){
     *     for(q = p->next; q; q = q->next){
                if(p->data < q->data){
                    swap(p,q,(unsigned long) (&((List_node *)0)->next));
                }
     *     }
     * }
     */
}

//打印
void        show_list(List_head head)
{
    if(head == NULL){
        return ;
    }

    List_node *node = NULL;
    node = head->next;

    while(node != NULL){
        printf("%d  ",node->data);
        node = node->next;
    }
    printf("\n");
}

//获取链表长度
int         get_list_length(List_head head)
{
    if(head == NULL){
        return -1;
    }

    return head->data;
}

设计完这个单链表的接口与实现之后,我们要对相应功能进行检测。
测试代码如下:

int main(int ac, char **av)
{
    int i = 0;
    int seed = 10;
    List_head head = NULL;
    head = init_list();
    List_node *node = create_node();

    printf("\n头部插入:\n");
    for(i = 0 ; i < seed ; i++ )
    {
        push_front(head,rand()%100);
    }
    show_list(head);  
    printf("\n尾部插入:\n"); 
    for(i = 0 ; i < seed ; i++ )
    {
        push_back(head,rand()%100);
    }
    show_list(head);
    printf("\n头部删除:\n"); 
    pop_front(head);
    show_list(head);
    printf("\n尾部删除:\n"); 
    pop_back(head);
    show_list(head);

    printf("\n插入节点:\n");
    insert_node(head, 3, 100);
    show_list(head);

    printf("\n查找节点值为77的节点: ");
    i = find_node(head, 77,&node);
    if(i == TRUE){
       printf("找到了\n");
    }els{
       printf("没找到\n");
    }

    printf("\n升序排序:\n");
    sort_list_ascend(head);
    show_list(head);

    printf("\n降序排序:\n");
    sort_list_descend(head);
    show_list(head);

    printf("\n修改节点数据:\n");
    node = head->next->next;
    modify_node(node, 777);
    show_list(head);

    printf("\nthe length of this list :%d\n",get_list_length(head));
    destroy_list(&head);
    return 0;
}

运行结果:

[root@linux-daemon 1]# gcc main.c list.c -o main
[root@linux-daemon 1]# ./main 

头部插入:
21  49  92  86  35  93  15  77  86  83  

尾部插入:
21  49  92  86  35  93  15  77  86  83  62  27  90  59  63  26  40  26  72  36  

头部删除:
49  92  86  35  93  15  77  86  83  62  27  90  59  63  26  40  26  72  36  

尾部删除:
49  92  86  35  93  15  77  86  83  62  27  90  59  63  26  40  26  72  

插入节点:
49  92  86  100  35  93  15  77  86  83  62  27  90  59  63  26  40  26  72  

查找节点值为77的节点: find

升序排序:
15  26  26  27  35  40  49  59  62  63  72  77  83  86  86  90  92  93  100  

降序排序:
100  93  92  90  86  86  83  77  72  63  62  59  49  40  35  27  26  26  15  

修改节点数据:
100  777  92  90  86  86  83  77  72  63  62  59  49  40  35  27  26  26  15  

the length of this list :19

完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值