数据结构与算法---(2)单向循环链表

384 阅读5分钟

循环链表:最后一个结点的(指针域)next指针指向首元结点,也就是p.next -> L

最后一个结点的next指针指向首元结点

typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */

//定义结点
typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;

typedef struct Node * LinkList;

单向链表的创建

  • 第一次创建

首先创建一个新结点;判断新结点是否为空;

  • 已经创建,新增数据

链表结尾的位置,尾节点的next->新结点;新结点的next=首元结点

c语言注修改本身加*,传地址; 不修改它本身不用加* 传值

代码部分:
/*
 1、链表是否已经存在
 2、若不存在,创建新节点,头指针指向新节点,新节点->next指向新节点
 3、若已存在,尾插法向后新增节点,新的尾节点->next指向首元节点,即*L
 */
Status CreateList(LinkList *L)
{
    int item;
    LinkList temp = NULL; // 每次创建的新节点
    LinkList fp = NULL; // 标记尾节点
    
    while (1) {
        
        scanf("%d",&item); // 输入创建的新值
        
        if (item == 0) break;  // 输入为0时,结束创建
        
        if (*L == NULL)
        {
            // 创建首元节点
            temp = (LinkList)malloc(sizeof(Node));
            if (temp == NULL) return 0;
            
            temp->data = item; // 向节点写入数据
            temp->next = temp; // 首元节点的next指向自己,因为只有自己一个节点
            
            fp = temp;  // 首元节点也是尾节点
            *L = temp;  // 头指针指向首元节点
        }
        else
        {
            // 向链表后追加新节点,并更新尾节点
            // 创建新节点
            temp = (LinkList)malloc(sizeof(Node));
            if (temp == NULL) return 0;
            temp->data = item;
            
            // 新节点next指向尾节点的next   因为是循环,尾节点的next其实就是*L,
            // temp->next = fp->next;
            temp->next = *L;
            
            // 尾节点指向新节点
            fp->next = temp;
            // 更新尾节点
            fp = temp;
        }
    }
    
    return 1;
}

int main(int argc, const char * argv[]) {
    
    LinkList head;
    // 创建
    CreateList(&head);
    // 输出链表
    PrintList(head);
    
    return 0;
}


遍历
void PrintList(LinkList L)
{
    if (L == NULL)
    {
        printf("链表为空!");
    }
    else
    {
        /*
         第一种遍历方式
         */
//        LinkList temp = L;
//        do{
//            printf("%5d",temp->data);
//            temp = temp->next;
//        }while (temp != L);
        
        /*
         第二种遍历方式
        */
//        LinkList temp = L;
//        while (temp->next != L) {
//            printf("%5d",temp->data);
//            temp = temp->next;
//        }
//        printf("%5d\n",temp->data);
        
        /*
         第三种遍历方式
        */
        LinkList temp = L;
        for (; temp->next!=L; temp = temp->next) {
            printf("%5d",temp->data);
        }
        printf("%5d\n",temp->data);
    }
}

二.循环链表的插入

代码:
temp = (LinkList)malloc(sizeof(Node));
if (temp == NULL) return ERROR;
temp->data = m;

// 1、找到尾节点target
for (target = *L; target->next != *L; target = target->next) ;
// 2、新节点指向首节点
temp->next = *L;
// 3、尾节点指向新节点
target->next = temp;
// 4、头指针指向新首元节点
*L = temp;

代码
int i; // 辅助确定插入位置

temp = (LinkList)malloc(sizeof(Node));
if (temp == NULL) return ERROR;
temp->data = m;

// 1、找到插入位置的前一个节点target
for (i = 1, target = *L; target->next != *L && i != index-1; i++,target = target->next) ;
// 2、新节点 指向 target的后节点
temp->next = target->next;
// 3、target 指向 新节点
target->next = temp;

完整代码

/// 插入数据
/// @param L 链表(链表的头指针)
/// @param index 插入位置 1是首元节点 所以插入位置要从1开始
/// @param m 插入的节点数据
Status ListInsert(LinkList *L, int index, int m)
{
    LinkList target = NULL; // 插入的节点的前一个节点
    LinkList temp = NULL;   // 新节点
    
    if (index < 1) return ERROR;
    
    if (index == 1)
    {
        temp = (LinkList)malloc(sizeof(Node));
        if (temp == NULL) return ERROR;
        
        temp->data = m;
  
        // 1、找到尾节点target
        for (target = *L; target->next != *L; target = target->next) ;
        
        // 2、新节点指向首节点
        temp->next = *L;
        // 3、尾节点指向新节点
        target->next = temp;
        // 4、头指针指向新首元节点
        *L = temp;
    }
    else
    {
        int i; // 辅助确定插入位置
        
        temp = (LinkList)malloc(sizeof(Node));
        if (temp == NULL) return ERROR;
        
        temp->data = m;
        
        // 1、找到插入位置的前一个节点target
        for (i = 1, target = *L; target->next != *L && i != index-1; i++,target = target->next) ;
        // 2、新节点 指向 target的后节点
        temp->next = target->next;
        // 3、target 指向 新节点
        target->next = temp;
    }
    
    return OK;
}

三. 循环链表的删除

两种情况:

删除首元结点

代码:

// 1、找到尾节点target
for (target = *L; target->next != *L; target = target->next);
temp = *L; // temp要删除的节点

// 2、尾节点指向删除节点后的节点
target->next = (*L)->next;

// 3、首指针指向尾节点后的节点
*L = target->next;

// 4、释放
free(temp);

删除其他结点

int i;
// 1、找到尾节点target  和 要删除的temp
for (i=1, target = *L; target->next != *L && i < index-1; target = target->next, i++);
temp = target->next;

// 2、target指向temp的后一个节点
target->next = temp->next;

// 3、释放
free(temp);

完整代码
/// 删除指定位置的节点
/// @param L 链表
/// @param index 位置
Status ListDelete(LinkList *L, int index)
{
    if (*L == NULL) return ERROR;
    
    // 只剩一个节点
    if ((*L)->next == *L) {

        (*L) = NULL;
        return ERROR;
    }
    
    LinkList target , temp;
    
    if (index == 1)
    {
        // 删除首元节点
        
        // 1、找到尾节点target
        for (target = *L; target->next != *L; target = target->next);
        temp = *L; // temp要删除的节点
        
        // 2、尾节点指向删除节点后的节点
        target->next = (*L)->next;
        // 3、首指针指向尾节点后的节点
        *L = target->next;
        // 4、释放
        free(temp);
    }
    else
    {
        // 删除其他任意节点
        
        int i;
        
        // 1、找到尾节点target  和 要删除的temp
        for (i=1, target = *L; target->next != *L && i < index-1; target = target->next, i++);
        temp = target->next;
        
        // 2、target指向temp的后一个节点
        target->next = temp->next;
        
        // 3、释放
        free(temp);
    }
    
    return OK;
}

四. 单项循环链表查询,查询倒数第K个,约瑟夫问题等(面试)

  • 单向循环链表查询
  • 查找单链表倒数第k个元素
    采用 快速查找思想

假设k=2,输出元素4。p1往前走k-1步。当p1指向表的尾时,p2正好是要输出的元素。

就是通过两个指针p1,p2,首先让p1往前走k-1步,然后让p1,p2一起往前走,当p1走到尾的时候,p2即为倒数第k个。总共走了n次。时间复杂度为O(n).

bool find(Linklist &list,int k){//遍历n次 
	LNode *p1,*p2;
	p1=p2=list->link;
	for(int i=1;i<=k-1;i++)//p1往前走k-1个(假设起始在倒数k处,往后走k-1个就到尾了)
		p1=p1->link;
	while(p1->link){
		p1=p1->link;
		p2=p2->link; 
	} 
	cout<<"date_k:"<<p2->date<<endl;
} 
  • 约瑟夫问题