单链表

  • 每个结点除了存档元素外,还要存储下一个结点的指针
  • 优点:不要求大片连续空间,改变容量方便
  • 缺点:不可随机存取

用代码定义一个单链表

struct LNode{			 //定义单链表结点类型
    ElemType data;		 //每个结点存放一个数据元素(数据域)
    struct LNode *next;  //指针指向下一个结点
};

增加一个新结点:在内存中申请一个结点需要空间,并用指针p指向这个结点

struct LNode *p = (struct LNode *)malloc(sizeof(struct LNode));
  • typedef关键字——数据类型重命名

    typedef struct LNode LNode;//先对struct LNode重命名为LNode,简便后续操作
    LNode *p = (LNode *)malloc(sizeof(LNode));
    
// 书本上更简洁的命名方法
typedef struct LNode{//定义单链表结点类型
	ElemType data;
	struct LNode *next;
}Node,*LinkList;//这两个都是struct LNode的重命名

上面的代码等价于

struct LNode{
	ElemType data;
	struct LNode *next;
};

typedef struct LNode LNode;
typedef struct LNpde *LinkList;

这样,如果要表示一个单链表时,只需要声明一个头指针L,指向单链表的第一个结点

LNode* L;
LinkList L;//或者这样都可以声明一个指向单链表第一个结点的头指针,这种当时代码可读性更强
  • 具体有什么区别,看下面的代码

    typedef struct LNode{
    	ElemType data;
        struct LNode *next;
    }LNode, *LinkList;
    
    LNode * GetElem(LinkList L, int i){//这里两种声明方式都用到了,其实LinkList L也完全可以用LNode *L来替换,但是为什么要这么写呢?原因是:——当我们使用LinkList时,我们强调这是一个单链表;——当我们使用LNode *时,我们强调这是一个结点
    	int j = 1;
        LNode * p = L->next;
        if(i == 0)
            return L;
        if(i < 1)
            return NULL;
        while(p != NULL && j < i){
     		p = p->next;
            j++;
        }
        return 0;
    }
    

初始化单链表

  • 不带头结点的单链表
typedef struct LNode{//定义单链表结点类型
    ElemType data;//每个结点存放一个数据元素
    struct LNode *next;//指针指向下一个结点
}LNode, *LinkList;

//初始化一个空的单链表
bool InitList(LinkList &L){//如果这里不用&引用符号,那么是改变不了主函数中的L的,即不能把主函数中的L初始化,改变的是主函数L的另为一个副本
	L = NULL;//空表,暂时还没有任何结点(防止脏数据)
    return true;
}

//判断单链表是否为空
bool Empty(LinkList L){
    if(L == NULL)
        return true;
    else
        return false;
}
//或者,因为L==NULL判断之后返回的就是true或者false所以,下面的写法更简洁
bool Empty(LinkList L){
	return(L==NULL);
}

void test(){
    LinkList L;//声明一个指向单链表的指针
    //初始化一个空表
    InitList(L);
    //....后续代码...
}
  • 带头结点的单链表
typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode, *LinkList;

//初始化一个单链表(带头结点)
bool InitList(LinkList &L){
	L = (LNode *)malloc(sizeof(LNode));//分配一个头结点
    if(L==NULL)//内存不足,分配失败
        return false;
    L->next = NULL;//头结点之后暂时还没有结点
    return true;
}

//判断单链表是否为空(带头结点)
bool Empty(LinkList L){
	if(L->next == NULL)
        return true;
    else
        return false;
}

void test(){
	LinkList L;//声明一个指向单链表的指针
    InitList(L);//初始化一个空表
    //....后续代码...
}
单链表_链表

带头结点的指针写代码会更加方便

单链表的插入删除

插入

  • 按位序插入:带头结点的插入ListInsert(&L, i,e):在表L中的第i个位置上插入指定元素e

    typedef struct LNode{
    	ElemType data;
    	struct LNode *next;
    }LNode, *LinkList;
    
    //在第i个位置插入元素e(带头结点)
    bool ListInsert(LinkList &L, int i, ElemType e){
        if(i<1)
            return false;
        LNode *p;//指针p指向当前扫描到的结点
        int j = 0;//当前p指向的是第几个结点
        p = L;//L指向头结点,头结点是第0个结点(不存数据)
        while(p!=NULL && j<i-1){//循环找到第i-1个结点
    		p = p->next;
            j++;
        }
        if(p==NULL)//i值不合法
            return false;
        LNode *s =(LNode *)malloc(sizeof(LNode));
        s->data = e;
        s->next = p->next;
        p->next = s;//将结点s连到p之后
        return true;//插入成功
    }
    
  • 按位序插入:不带头结点的插入:找到第i-1个结点,把新结点插在其后面。不存在“第0个”结点,因此当i=1的时候需要特殊处理

    typedef struct LNode{
    	ElemType data;
    	struct LNode *next;
    }LNode, *LinkList;
    
    //在第i个位置插入元素e(带头结点)
    bool ListInsert(LinkList &L, int i, ElemType e){
        if(i<1)
            return false;
        if(i == 1){//插入第1个结点的操作与其他结点操作不同
    		LNode *s = (LNode *)malloc(sizeof(LNode));
            s->data = e;
            s->next = L;
            L = s;//头指针指向新节点
        }
        
        LNode *p;//指针p指向当前扫描到的结点
        int j = 1;//当前p指向的是第几个结点
        p = L;//L指向头结点,头结点是第0个结点(不存数据)
        while(p!=NULL && j<i-1){//循环找到第i-1个结点
    		p = p->next;
            j++;
        }
        if(p==NULL)//i值不合法
            return false;
        LNode *s =(LNode *)malloc(sizeof(LNode));
        s->data = e;
        s->next = p->next;
        p->next = s;//将结点s连到p之后
        return true;//插入成功
    }
    
  • 后插操作:在p结点之后插入元素e

    bool InsertNextNode (LNode *p, ElemType e){
    	if(p == NULL)
            return false;
        LNode *s = (LNode *)malloc(sizeof(LNode));
        //判断内存分配是否成功(某些情况下有可能分配失败,如内存不足,但是考试手写时也可以不写)
        if(s==NULL)
            return false;
        s->data = e;
        s->next = p->next;
        p->next = s;
        return true;
    }
    
  • 前插操作:在p结点之前插入元素e

    bool InsertPriorNode(LNode *p, LNode *s){
    	if(p==NULL || s = NULL)
            return false;
        s->next = p->next;
        p->next = s;
        ElemType temp = p->data;
        p->data = s->data;
        s->data = temp;
        return false;
    }
    

删除

  • ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值
typedef struct LNode{
	ElemType data;
	struct LNode *next;
}LNode, *LinkList;

//在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L, int i, ElemType e){
    if(i<1)
        return false;
    LNode *p;//指针p指向当前扫描到的结点
    int j = 0;//当前p指向的是第几个结点
    p = L;//L指向头结点,头结点是第0个结点(不存数据)
    while(p!=NULL && j<i-1){//循环找到第i-1个结点
		p = p->next;
        j++;
    }
    if(p==NULL)//i值不合法
        return false;
    LNode *q = p->next;
    e = p->data;
    p->next = q->next;
    free(q);
    return true;
  • 删除指定结点

    如果p结点是最后一个结点的话,p->next->data = NULL,就会出现空指针的错误,如果出现这种情况,那我们就只能引入头指针,来从头开始一个个搞,

    bool DeleteNode(LNode *p){
        if(p==NULL)
            return false;
        LNode *q = p->next;
        p->data = p->next->data;
        p->next = q->next;
        free(q);
        return true;    
    }
    

单链表的查找

  • 按位查找:GetElem(L,i)。获取表L中第i个位置的元素的值

    //带头结点,返回第i个元素
    LNode * GetElem(LinkList L,int i){
    	if(i<0)
    		return NULL;
    	LNode *p;//指针p指向当前扫描到的结点
        int j = 0;//当前p指向的是第几个结点
        p = L;//L指向头结点,头结点是第0个结点(不存数据)
        while(p!=NULL && j<i){//循环找到第i个结点
    		p = p->next;
            j++;
        }
        return p;
    }
    
  • 按值查找:LocateElem(L,e)。在表L中查找具有给定个关键字值的元素

    //按值查找,找到数据域==e的结点
    LNode *LocateElem(LinkList L,ElemType e){
    	LNode *p = L->next;//从第一个结点开始查找数据域为e的结点
    	while(p!=NULL && p->data !=e)
    		p = p->next;
    	return p;//找到后返回该结点指针,否则返回NULL
    }
    

求表的长度

int Length(LinkList L){
	int len = 0;
	LNode *p = L;
	while(p->next != NULL){
		p = p->next;
		len++;
	}
	return len;
}