数据结构day3

发布于:2025-05-17 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、gdb调试


gcc -g main.c linklist.c    //  对两个.c文件进行编译,生成 a.out 文件 

gdb a.out                         //调试可执行文件 a.out

b linklist.c:36             // 在该.c文件第 36 行设置断点

r                                //  运行程序,但会在断点前停下

n                               //执行下一步

s                               //若遇到函数调用,可通过s进入该函数调用内部执行过程

p  len                        //显示变量值

q                                //退出调试

     

二.单链表的基本操作

linklist.h文件        主要是包含数据和函数声明

#ifndef _LINKLIST_H_      //linklist.h文件,主要包含 数据 和 函数的声明
#define _LINKLIST_H_
typedef struct     //链表中数据 的 抽象数据类型
{
    char name[10];
    char sex;
    int age;
    int score;
}DATATYPE;

typedef struct _node_   //链表结点声明
{ 
    DATATYPE data;
    struct _node_ * next;
}LinkNode;

typedef struct     
{
    LinkNode * head;  //链表头指针
    int clen          //链表长度   
}LinkList;

extern LinkList*CreateLinkList();  //创建链表
extern int InsertHeadLinkList(LinkList*ll,DATATYPE*data);//链表头插
extern int InsertTailLinkList(LinkList*ll,DATATYPE*data);//链表尾插
extern int InsertPosLinkList(LinkList*ll,DATATYPE*data,int pos);//链表指定位置插,从0开始

extern int IsEmptyLinkList(LinkList*ll); //链表判空
extern int GetSizeLinkList(LinkList*ll); //获取链表长度
extern int ShowLinkList(LinkList*ll);    //打印链表数据
extern DATATYPE* FindLinkList(LinkList*ll,char*name);//根据名字找链表中的数据
extern int ModifyLinkList(LinkList*ll,char*name,DATATYPE*data); //修改链表数据
extern int DeleteLinkList(LinkList*ll,char*name);  //删除链表节点
extern int DestroyLinkList(LinkList**ll);   //销毁链表

extern LinkNode* FindMidLinkList(LinkList*ll);      //找中间结点
extern LinkNode* FindLastKLinkList(LinkList*ll,int k);   //找倒数第k个结点
extern int RevertLinkList(LinkList*ll);             //反转结点
extern int InsertSortLinkList(LinkList*ll);         //用插入排序,依据成绩对链表排序
#endif

linklist.c文件        主要是包含函数的定义

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

LinkList* CreateLinkList()           //初始化创建链表
{
  LinkList* ll = malloc(sizeof(LinkList));   //为包含头指针的数据结构申请空间
  if (NULL == ll)                            //如果申请失败则,报错并退出
    {
      fprintf(stderr, "CreateLinkList malloc");
      return NULL;
    }

  ll->head = NULL;    //初始指向空
  ll->clen = 0;        //长度初始为0
  return ll;
}


int IsEmptyLinkList(LinkList* ll)  //对链表判空
{ 
    return 0 == ll->clen; 
}


int InsertHeadLinkList(LinkList* ll, DATATYPE* data) //对链表头插
{
  LinkNode* newnode = malloc(sizeof(LinkNode));   //申请一个待插入结点空间,
  if (NULL == newnode)                            //判断是否申请成功
    {
      fprintf(stderr, "InsertHeadLinkList malloc");
      return 1;
    }

  memcpy(&newnode->data, data, sizeof(DATATYPE));   //将数据拷贝到 新申请结点的数据域中
  newnode->next = NULL;                         //新结点指针域设置为NULL

  if (IsEmptyLinkList(ll))         //如果链表为空
    {
      ll->head = newnode;         //直接让头指针指向新结点
    }
  else
    {
      newnode->next = ll->head;    //否则,新结点指向,头指针指向的下一个结点

      ll->head = newnode;          //   头指针指向新结点
    }
  ll->clen++;                      //结点数量+1
  return 0;
}


int GetSizeLinkList(LinkList* ll)   //获得结点长度
{ 
    return ll->clen; 
}


int ShowLinkList(LinkList* ll)   //打印结点
{
  int len = GetSizeLinkList(ll);
  LinkNode* tmp = ll->head;

  int i = 0;
  for (i = 0; i < len; ++i)
    {
      printf("%s %c %d %d\n", tmp->data.name, tmp->data.sex, tmp->data.age,
             tmp->data.socre);
      tmp = tmp->next;
    }
  return 0;
}




DATATYPE* FindLinkList(LinkList* ll, char* name)   //根据name找结点
{
  LinkNode* tmp = ll->head;
  while (tmp)                  //当结点为NULL时,while循环退出
    {
      if (0 == strcmp(tmp->data.name, name))   //如果相等,则返回该结点数据域,并退出
        {
          return &tmp->data;
        }
      tmp = tmp->next;    //不相等,则指向下一个结点
    }
  return NULL;   //如果没找到返回NULL
}



int DeleteLinkList(LinkList* ll, char* name)    //根据数据域中的名字,删除结点
{
  LinkNode* tmp = ll->head;
  if (IsEmptyLinkList(ll))  //如果链表为空,直接返回1
    {
      return 1;
    }
  if (0 == strcmp(tmp->data.name, name))  //如果第一个就是
    {
      ll->head = ll->head->next;  
      free(tmp);    //释放结点
      ll->clen--;
      return 0;
    }
  while (tmp->next)   //当下一个节点为空时退出,因此上面把第一个结点单独拿出来写
    {
      if (0 == strcmp(tmp->next->data.name, name))
        {
          LinkNode* tmp2 = tmp->next;
          tmp->next = tmp->next->next;
          free(tmp2);
          ll->clen--;
          return 0; //找到返回0
        }
      tmp = tmp->next;
    }
  return 1;  //未找到返回1
}



int InsertTailLinkList(LinkList* ll, DATATYPE* data)   //尾插,与头插类似
{
  if (IsEmptyLinkList(ll))  //链表如果为空
    {
      return InsertHeadLinkList(ll, data); //直接调头插
    }
  else
    {
      LinkNode* newnode = malloc(sizeof(LinkNode));
      if (NULL == newnode)
        {
          fprintf(stderr, "InsertTailLinkList malloc");
          return 1;
        }

      memcpy(&newnode->data, data, sizeof(DATATYPE));
      newnode->next = NULL;

      LinkNode* tmp = ll->head;
      while (tmp->next)
        {
          tmp = tmp->next;  // tmp++;
        }
      tmp->next = newnode;
      ll->clen++;
    }
  return 0;
}


int InsertPosLinkList(LinkList* ll, DATATYPE* data, int pos)  //指定位置插入数据
{
  int len = GetSizeLinkList(ll); //得到链表长度

  if (pos < 0 || pos > len)  //如果插入位置不在该区间内,直接返回1,说明错误
    {
      return 1;
    }
  if (0 == pos)  // head 头部插入
    {
      return InsertHeadLinkList(ll, data);
    }
  else if (pos == len)  // tail  尾部插入
    {
      return InsertTailLinkList(ll, data);
    }
  else
    {
      LinkNode* newnode = malloc(sizeof(LinkNode));  申请结点
      if (NULL == newnode)
        {
          fprintf(stderr, "InsertPosLinkList malloc");
          return 1;
        }
      memcpy(&newnode->data, data, sizeof(DATATYPE));
      newnode->next = NULL;

      int i = 0;  
      LinkNode* tmp = ll->head;    
      for (i = 0; i < pos - 1; ++i)   让指针指向要插入位置的前一个结点
        {
          tmp = tmp->next;
        }
      newnode->next = tmp->next;
      tmp->next = newnode;
    }
  ll->clen++;
  return 0;
}


int ModifyLinkList(LinkList* ll, char* name, DATATYPE* data)  //修改指针中的数据
{
  DATATYPE* tmp = FindLinkList(ll, name);  
  if (NULL == tmp)
    {
      return 1;
    }
  memcpy(tmp, data, sizeof(DATATYPE));
  return 0;
}


int DestroyLinkList(LinkList** ll)  //销毁链表
{
  while (1)                            //需要先一个一个结点free最后free(linklist)
    {
      LinkNode* tmp = (*ll)->head;
      if (NULL == tmp)
        {
          break;
        }
      (*ll)->head = (*ll)->head->next;
      free(tmp);
    }
  free(*ll);
  *ll = NULL;

  return 0;
}
//可通过 valgrind ./执行文件名  看内存是否泄露,即没有free干净


LinkNode* FindMidLinkList(LinkList* ll)  //找链表中间结点
{
  LinkNode* fast = ll->head;  //申请两个指针,一个一次走一步,一个一次走两步,走两步的走完链表时
  LinkNode* slow = ll->head;  //,走一步的指针刚好到中间。

  while (fast)
    {
      fast = fast->next;
      if (NULL == fast)
        {
          break;
        }
      slow = slow->next;
      fast = fast->next;
    }

  return slow;
}

/**
 * @brief 查找倒数第k个节点
 *
 * @param ll 需要查找的对应的链表
 * @param k 倒数第k个节点 k>0   <len
 * @return LinkNode* 找到对应的节点的指针
 */

LinkNode* FindLastKLinkList(LinkList* ll, int k) //与找中间的类似,只是,走的快的和走的慢的指针
{
  LinkNode* slow = ll->head;                     //始终保持 k 个结点距离
  LinkNode* fast = ll->head;

  for (int i = 0; i < k; i++)
    {
      fast = fast->next;
    }

  while (fast)
    {
      fast = fast->next;
      slow = slow->next;
    }
  return slow;
}

int RevertLinkList(LinkList* ll)     //逆序链表结点
{
  LinkNode* prev = NULL;        //指向 逆序结点的前一个结点
  LinkNode* tmp = ll->head;     //指向 逆序的结点
  LinkNode* next = tmp->next;   //指向 逆序结点的下一个结点
  int len = GetSizeLinkList(ll);
  if (len < 2)            //如果链表长度 <2 则不需要逆序
    {
      return 1;
    }
  while (1) 
    {
      tmp->next = prev; 
      prev = tmp;
      tmp = next;
      if (NULL == tmp)  //当指向 需要逆序的结点指针为空时,说明没有结点了,跳出循环
      break;
      next = next->next;
    }
  ll->head = prev;
  return 0;
}

int InsertSortLinkList(LinkList* ll)  //对链表进行插入排序,依据年龄
{
  LinkNode*pins = ll->head;    //三个指针,pins指向头结点,ptmp指pins指向结点的下一个
  LinkNode* ptmp = pins->next; //Next指向pins指向结点的下一个结点。
  LinkNode* Next = ptmp->next;
  ptmp->next = NULL;   //置空
  ll->head->next = NULL; //置空

  while(1)
  {
    pins = ll->head;
    while(pins->next && ptmp->data.age > pins->data.age )   //pins的下一个结点存在 并且下一个
    {                                            //结点的age > pins 的age 则pins指向下一个
      pins = pins->next;                                      
    } 

    if(pins->data.age > ptmp->data.age) //head   如果第是头节点的下一个结点需要插入排序
    {
      ptmp->next = ll->head;
       ll->head = ptmp;
    }
    else
    {
      ptmp->next = pins->next;
      pins->next = ptmp;
    }

    ptmp = Next;
    if(NULL == ptmp) { break; }
    Next = Next->next;
    ptmp->next = NULL;
  }
  return 0;
}

三、顺序表和链表对比


1、存储方式
        顺序表 是一段连续的存储单元


        链表 是逻辑结构连续物理结构(在内存中的表现形式)不连续(因为是通过malloc获得的)

2、 时间性能
        查找 :          顺序表O(1)      //按规则放,找的时候按规则找;支持随机访问

                              链表  O(n)        //只能从第一个往后找

        插入和删除:顺序表 O(n)      //整体前移与后移

                             链表   O(1)        //一次性改(只需把一个节点连上),与链表数据量无关

3、 空间性能
        顺序表 需要预先分配空间,大小固定;只放数据
        链表, 不需要预先分配,大小可变,动态分配;数据 + 指针域


网站公告

今日签到

点亮在社区的每一天
去签到