前段时间跟一个同事去面试,他讲到他上次笔试的时候有个题目是单向链表反转, 他说当时他做不出来。 当时稍微思考了下, 应该不难实现, 心里想着有时间做它一做。
刚好今天闲着没事做, 就决定把它写一写。 咱不是个聪明的人, 我首先想到的方法就是要一个数组把链表保存下来, 然后从数组尾部到首部建表。这个方法直观,不好的是需要额外的数组,当链表的长度很长的时候,这个数组消耗会很大。我们先看下大概出来的图示:
因为比较直观,也无需做解释了, 直接贴上代码:
oneway_list_t *oneway_list_revert1 (
oneway_list_t *apList
)
{
oneway_list_t **array;
oneway_list_t *temp;
oneway_list_t *newHead;
int i = 0, j = 0;
if (NULL == apList) {
DERROR("[%s][%d] Error: empty parameter\n");
return NULL;
}
// caculate count
i = 1;
for (temp = apList->next; temp; temp = temp->next) {
i++;
}
DEBUG("list length = %d \n", i);
if (i == 1) {
return apList;
}
array = (oneway_list_t **)malloc(sizeof(oneway_list_t *) * i);
if (NULL == array) {
DERROR("[%s][%d] Error: No memory\n", __FUNCTION__, __LINE__);
return NULL;
}
//store list to array
i = 0;
for (temp = apList; temp; temp = temp->next) {
array[i++] = (oneway_list_t *)temp;
}
// revert list from array
newHead = array[i - 1];
temp = newHead;
for (j = i - 1; j >= 0; j--) {
temp->data = array[j]->data;
if (j == 0) {
temp->next = NULL;
} else {
temp->next = array[j - 1];
temp = temp->next;
}
}
free(array);
return newHead;
}
这个方法做完之后, 考虑到需要额外数组来实现,如果链表很长消耗内存会比较大。于是就考虑是否不需要额外数组就可以实现,脑子里就想能不能把now的next指向前一个,next的next指向now,做一次遍历就可以解决? 简单够了个图,觉得这个方式可行。 看下我画的简图:
这个图的思想是:
取连续的三个元素,命名为now, next, head. 修改这三个元素指向下一个的指针。
结构如下: now->next->head
now指向它之前的元素,next指向now, 下一个now从head开始. 遍历这个链表按照这样的方式修改
实现的难点就是下一轮的now的next要指向到前一轮的next。 看代码:
oneway_list_t * oneway_list_revert2 (
oneway_list_t *apList
)
{
oneway_list_t *head, *now, *next, *temp;
int i = 0;
if (NULL == apList)
return NULL;
if (apList->next == NULL)
return apList;
now = apList;
next = now->next;
now->next = NULL;
do {
if (next != NULL) {
head = next->next;
next->next = now;
} else {
head = next; // new head
next->next = now;
break;
}
if (head != NULL) {
now = head;
temp = next;
next = now->next;
now->next = temp;
} else {
head = next; // new head
break;
}
} while (1);
return head;
}
写了两个测试代码:
void test1(void)
{
oneway_list_t *head;
oneway_list_t *temp;
int i = 0;
int data[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
// create one-way list from array
head = new_node();
temp = head;
for (i = 0; i < 10; i++) {
temp->data = data[i];
if (i < 9) {
temp->next = new_node();
temp = temp->next;
} else {
temp->next = NULL;
}
}
DTRACE("Before revert : \n");
dump_list(head);
temp = oneway_list_revert1(head);
DTRACE("After revert : \n");
dump_list(temp);
free_list(temp);
}
void test2(void)
{
oneway_list_t *head;
oneway_list_t *temp;
int i = 0;
head = new_node();
temp = head;
for (i = 0; i < 20; i++) {
temp->data = i;
if (i < 19) {
temp->next = new_node();
temp = temp->next;
} else {
temp->next = NULL;
}
}
DTRACE("Before revert : \n");
dump_list(head);
temp = oneway_list_revert2(head);
DTRACE("After revert : \n");
dump_list(temp);
free_list(temp);
}
为了直观,我把链表的地址打印了出来:
完整的代码:
#include <stdio.h>
#include <stdlib.h>
#if 1
#define DEBUG printf
#else
#define DEBUG(...)
#endif
#define DERROR printf
#define DTRACE printf
typedef struct oneway_list_s {
int data;
struct oneway_list_s *next;
} oneway_list_t;
oneway_list_t *new_node(void) {
oneway_list_t *node = (oneway_list_t *)malloc(sizeof(oneway_list_t));
if (NULL == node)
return NULL;
memset(node, 0, sizeof(oneway_list_t));
node->next = NULL;
return node;
}
void free_list(oneway_list_t* apList)
{
oneway_list_t *now, *next;
int i = 0;
if (NULL == apList)
return;
now = apList;
do {
next = now->next;
if (NULL != now) {
free(now);
}
now = next;
} while (next);
}
/**
× @brief 单向链表反转
* 基本思想:用一个数组保存链表,再从数组尾部到首部构造新链表
*/
oneway_list_t *oneway_list_revert1 (
oneway_list_t *apList
)
{
oneway_list_t **array;
oneway_list_t *temp;
oneway_list_t *newHead;
int i = 0, j = 0;
if (NULL == apList) {
DERROR("[%s][%d] Error: empty parameter\n");
return NULL;
}
// caculate count
i = 1;
for (temp = apList->next; temp; temp = temp->next) {
i++;
}
DEBUG("list length = %d \n", i);
if (i == 1) {
return apList;
}
array = (oneway_list_t **)malloc(sizeof(oneway_list_t *) * i);
if (NULL == array) {
DERROR("[%s][%d] Error: No memory\n", __FUNCTION__, __LINE__);
return NULL;
}
//store list to array
i = 0;
for (temp = apList; temp; temp = temp->next) {
array[i++] = (oneway_list_t *)temp;
}
// revert list from array
newHead = array[i - 1];
temp = newHead;
for (j = i - 1; j >= 0; j--) {
temp->data = array[j]->data;
if (j == 0) {
temp->next = NULL;
} else {
temp->next = array[j - 1];
temp = temp->next;
}
}
free(array);
return newHead;
}
/**
* @brief 单向链表反转
× 取连续的三个元素,命名为now, next, head. 修改这三个元素指向下一个的指针。
× 结构如下: now->next->head
× now指向它之前的元素,next指向now, 下一个now从head开始. 遍历这个链表按照这样的方式修改
*/
oneway_list_t * oneway_list_revert2 (
oneway_list_t *apList
)
{
oneway_list_t *head, *now, *next, *temp;
int i = 0;
if (NULL == apList)
return NULL;
if (apList->next == NULL)
return apList;
now = apList;
next = now->next;
now->next = NULL;
do {
if (next != NULL) {
head = next->next;
next->next = now;
} else {
head = next; // new head
next->next = now;
break;
}
if (head != NULL) {
now = head;
temp = next;
next = now->next;
now->next = temp;
} else {
head = next; // new head
break;
}
} while (1);
return head;
}
void dump_list(oneway_list_t *apList)
{
oneway_list_t *now, *next;
int i = 0;
if (NULL == apList)
return;
now = apList;
do {
next = now->next;
DTRACE("list[%d] : addr[%d], data[%d]\n", i++, now, now->data);
now = next;
} while (now);
}
void test1(void)
{
oneway_list_t *head;
oneway_list_t *temp;
int i = 0;
int data[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
// create one-way list from array
head = new_node();
temp = head;
for (i = 0; i < 10; i++) {
temp->data = data[i];
if (i < 9) {
temp->next = new_node();
temp = temp->next;
} else {
temp->next = NULL;
}
}
DTRACE("Before revert : \n");
dump_list(head);
temp = oneway_list_revert1(head);
DTRACE("After revert : \n");
dump_list(temp);
free_list(temp);
}
void test2(void)
{
oneway_list_t *head;
oneway_list_t *temp;
int i = 0;
head = new_node();
temp = head;
for (i = 0; i < 20; i++) {
temp->data = i;
if (i < 19) {
temp->next = new_node();
temp = temp->next;
} else {
temp->next = NULL;
}
}
DTRACE("Before revert : \n");
dump_list(head);
temp = oneway_list_revert2(head);
DTRACE("After revert : \n");
dump_list(temp);
free_list(temp);
}
void main(void)
{
test1();
test2();
}