Delete from End (Circular Linked List)

In a singly circular list, the last node is easy to reach via tail, but its predecessor is not—so you walk from head until cur->next == tail. Then point the predecessor at tail->next (first node), free(tail), and return the predecessor as the new tail. Time O(n) in the list length.

C program (return new tail)

Build 10 20 30 with insert_end, then call delete_end three times. Assign tail = delete_end(tail); each time.

  • One node: free(tail), return NULL.
  • Otherwise: find second-to-last, close ring to head, free old tail, return new tail.
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *insert_end(struct Node *tail, int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    if (newNode == NULL)
        return tail;
    newNode->data = data;
    if (tail == NULL) {
        newNode->next = newNode;
        return newNode;
    }
    newNode->next = tail->next;
    tail->next = newNode;
    return newNode;
}

struct Node *delete_end(struct Node *tail)
{
    if (tail == NULL)
        return NULL;
    struct Node *head = tail->next;
    if (head == tail) {
        free(tail);
        return NULL;
    }
    struct Node *cur = head;
    while (cur->next != tail)
        cur = cur->next;
    cur->next = tail->next;
    free(tail);
    return cur;
}

void print_list(struct Node *tail)
{
    if (tail == NULL) {
        printf("\n");
        return;
    }
    struct Node *head = tail->next;
    struct Node *cur = head;
    do {
        printf("%d ", cur->data);
        cur = cur->next;
    } while (cur != head);
    printf("\n");
}

int main(void)
{
    struct Node *tail = NULL;

    tail = insert_end(tail, 10);
    tail = insert_end(tail, 20);
    tail = insert_end(tail, 30);

    print_list(tail);   /* 10 20 30 */

    tail = delete_end(tail);
    tail = delete_end(tail);
    tail = delete_end(tail);

    print_list(tail);   /* empty list: newline only */

    return 0;
}

Program output

10 20 30

First print_list shows the list. After three delete_end calls, the second print_list prints only a newline (no numbers).

Walk to second-to-last

head → 10 → 20 → 30 ─┘
              ▲ cur stops here (cur→next == tail)
              └── new tail after delete

Complexity summary

Operation Time Notes
delete_endO(n)Find predecessor of tail.
delete_beginO(1)Contrast: delete from beginning.

Compare Delete from End (SLL) — same predecessor walk, but CLL links the predecessor back to head instead of NULL.