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), returnNULL. - 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_end | O(n) | Find predecessor of tail. |
delete_begin | O(1) | Contrast: delete from beginning. |
Compare Delete from End (SLL) — same predecessor walk, but CLL links the predecessor back to head instead of NULL.