三、编码技巧

1、遍历链表

先将head指针赋值给一个局部变量current

//return the number of nodes in a list (while-loop version)
int Length(struct node* head)
{
int count = 0;
struct node* current = head; while (current != NULL)
{
count++;
current = current->next;
} return count;
}

当然也可以写为:

for (current = head; current != NULL; current = current->next) {}

2、通过传递reference pointer改变某个指针

看个例子:

//Change the passed in head pointer to be NULL
//Uses a reference pointer to access the caller's memory
void ChangeToNull(struct node** headRef) //takes a pointer to the value of interest
{
*headRef = NULL; //use * to access the value of interest
} void ChangeCaller()
{
struct node* head1;
struct node* head2; ChangeToNull(&head1); //use & to compute and pass a pointer to
ChangeToNull(&head2); //the value of interest
//head1 and head2 are NULL at this point
}

这块的思想是和(一)中的Push()类似。

内存示意图:



3、通过Push()建立链表(头插法)

这种方式的优点是速度飞快,简单易行,缺点是得到的链表是逆序的:

struct node* AddAtHead()
{
struct node* head = NULL; for (int i = 1; i < 6; i++)
{
Push(&head, i);
} //head == {5,4,3,2,1};
return head;
}

4、尾插法建立链表

这种方法需要找到链表最后一个节点,改变其.next域:

  • 插入或者删除节点,需要找到该节点的前一个节点的指针,改变其.next域;
  • 特例:如果涉及第一个节点的操作,那么一定要改变head指针。

5、特例+尾插法

如果要构建一个新的链表,那么头节点就要单独处理:

struct node* BuildWithSpecialCase()
{
struct node* head = NULL;
struct node* tail; //deal with the head node here, and set the tail pointer
Push(&head, 1);
tail = head; //do all the other nodes using "tail"
for (int i = 2; i < 6; i++)
{
Push(&(tail->next), i); //add node at tail->next
tail = tail->next; //advance tail to point to last node
} return head; //head == {1,2,3,4,5}
}

6、临时节点建立

struct node* BuildWithDummyNode()
{
struct node dummy; //dummy node is temporarily the first node
struct node* tail = &dummy; //build the list on dummy.next dummy.next = NULL; for (int i = 1; i < 6; i++)
{
Push(&(tail->next), i);
tail = tail->next;
} //the real result list is now in dummy.next
//dummy.next == {1,2,3,4,5}
return dummy.next;
}

7、本地指针建立

struct node* BuildWithLocalRef()
{
struct node* head = NULL;
struct node** lastPtrRef = &head; //start out pointing to the head pointer for (int i = 1; i < 6; i++)
{
Push(lastPtrRef, i); //add node at the last pointer in the list
//advance to point to the new last pointer
lastPtrRef = &((*lastPtrRef)->next);
} return head; //head == {1,2,3,4,5}
}

这块可能有些抽象:

1)lastPtrRef开始指向head指针,以后指向链表最后一个节点中的.next域;

2)在最后加上一个节点;

3)让lastPtrRef指针向后移动,指向最后一个节点的.next(*lastPtrRef)->next可以理解为*lastPtrRef指针指向的节点的next域。

四、代码示例

1、AppendNode()

  1. 不使用Push()函数:
struct node* AppendNode(struct node** headRef, int num)
{
struct node* current = *headRef;
struct node* newNode; newNode = (struct node*)malloc(sizeof(struct node));
newNode->data = num;
newNode->next = NULL; //special case for length 0
if (current == NULL)
{
*headRef = newNode;
}
else
{
//Locate the last node
while (current->next != NULL)
{
current = current->next;
} current->next = newNode;
}
}
  1. 使用Push()函数:
struct node* AppendNode(struct node** headRef, int num)
{
struct node* current = *headRef; //special case for length 0
if (current == NULL)
{
Push(headRef, num);
}
else
{
//Locate the last node
while (current->next != NULL)
{
current = current->next;
} //Build the node after the last node
Push(&(current->next), num);
}
}

2、CopyList

用一个指针遍历原来的链表,两个指针跟踪新生成的链表(一个head,一个tail)。

  1. 不使用Push()函数:
struct node* CopyList(struct node* head)
{
struct node* current = head; //used to iterate over the original list
struct node* newList = NULL; //head of the new list
struct node* tail = NULL; //kept pointing to the last node in the new list while (current != NULL)
{
if (newList == NULL) //special case for the first new node
{
newList = (struct node*)malloc(sizeof(struct node));
newList->data = current->data;
newList->next = NULL;
tail = newList;
}
else
{
tail->next = (struct node*)malloc(sizeof(struct node));
tail = tail->next;
tail->data = current->data;
tail->next = NULL;
}
current = current->next;
} return newList;
}

内存示意图:



2) 使用Push()函数:

struct node* CopyList2(struct node* head)
{
struct node* current = head; //used to iterate over the original list
struct node* newList = NULL; //head of the new list
struct node* tail = NULL; //kept pointing to the last node in the new list while (current != NULL)
{
if (newList == NULL) //special case for the first new node
{
Push(&newList, current->data);
tail = newList;
}
else
{
Push(&(tail->next), current->data); //add each node at the tail
tail = tail->next; //advance the tail to the new last node;
}
current = current->next;
} return newList;
}
  1. 使用Dummy Node
struct node* CopyList3(struct node* head)
{
struct node* current = head; //used to iterate over the original list
struct node* tail = NULL; //kept pointing to the last node in the new list
struct node dummy; //build the new list off this dummy node dummy.next = NULL;
tail = &dummy; //start the tail pointing at the dummy while (current != NULL)
{
Push(&(tail->next), current->data); //add each node at the tail
tail = tail->next; //advance the tail to the new last node
current = current->next;
} return dummy.next;
}
  1. 使用Local References
struct node* CopyList4(struct node* head)
{
struct node* current = head; //used to iterate over the original list
struct node* newList = NULL; //head of the new list
struct node** lastPtr; lastPtr = &newList; //start off pointing to the head itself while (current != NULL)
{
Push(lastPtr, current->data); //add each node at the lastPtr
lastPtr = &((*lastPtr)->next); //advance lastPtr
current = current->next;
} return newList;
}

核心思想是使用lastPtr指针指向每个节点的.next域这个指针,而不是指向节点本身。

  1. 使用Recursive
struct node* CopyList5(struct node* head)
{
struct node* current = head;
if (head == NULL)
return NULL;
else {
struct node* newList = (struct node*)malloc(sizeof(struct node)); //make one node
newList->data = current->data; newList->next = CopyList5(current->next); //recur for the rest
return newList;
}
}

Linked List-2的更多相关文章

  1. [LeetCode] Linked List Random Node 链表随机节点

    Given a singly linked list, return a random node's value from the linked list. Each node must have t ...

  2. [LeetCode] Plus One Linked List 链表加一运算

    Given a non-negative number represented as a singly linked list of digits, plus one to the number. T ...

  3. [LeetCode] Odd Even Linked List 奇偶链表

    Given a singly linked list, group all odd nodes together followed by the even nodes. Please note her ...

  4. [LeetCode] Delete Node in a Linked List 删除链表的节点

    Write a function to delete a node (except the tail) in a singly linked list, given only access to th ...

  5. [LeetCode] Palindrome Linked List 回文链表

    Given a singly linked list, determine if it is a palindrome. Follow up: Could you do it in O(n) time ...

  6. [LeetCode] Reverse Linked List 倒置链表

    Reverse a singly linked list. click to show more hints. Hint: A linked list can be reversed either i ...

  7. [LeetCode] Remove Linked List Elements 移除链表元素

    Remove all elements from a linked list of integers that have value val. Example Given: 1 --> 2 -- ...

  8. [LeetCode] Intersection of Two Linked Lists 求两个链表的交点

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  9. [LeetCode] Linked List Cycle II 单链表中的环之二

    Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...

  10. [LeetCode] Linked List Cycle 单链表中的环

    Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using ex ...

随机推荐

  1. 在 Array.filter 中正确使用 Async

    本文译自How to use async functions with Array.filter in Javascript - Tamás Sallai. 0. 如何仅保留满足异步条件的元素 在第一 ...

  2. CVPR2018关键字分析生成词云图与查找

    今日目标:爬取CVPR2018论文,进行分析总结出提到最多的关键字,生成wordCloud词云图展示,并且设置点击后出现对应的论文以及链接 对任务进行分解: ①爬取CVPR2018的标题,简介,关键字 ...

  3. python3中的nonlocal 与 global

    nonlocal 与 global nonlocal翻译是非本地,global翻译是全局,它们都是python3的新特性.如果以类C语言的思维去看这2个关键字,很可能觉得它们差不多.但实际上它们很不一 ...

  4. replace into 影响行数

    replace into 影响行数,谈起影响行数,先理解replace into 原理:其是先到表里通过一定规则(单主键或复合主键或唯一索引)找到记录,并且删除,然后在insert into 记录,即 ...

  5. view事件分发源码理解

    有些困难无法逃避,没办法,那就只有去解决它.view事件分发对我而言是一块很难啃的骨头,看了<安卓开发艺术探索>关于这个知识点的讲解,看了好几遍,始终不懂,最终通过调试分析结果,看博客,再 ...

  6. 【jmeter】JDBC请求循环调用的问题

    今天使用jdbc请求从数据库取数据,多次请求使用了循环控制器,但是结果第一个jdbc请求返回值正确,第二次请求返回值为空. 1.从其他博客中得知,需要在jdbc connection configur ...

  7. golang方法详解

    Go 语言 类型方法是一种对类型行为的封装 .Go 语言的方法非常纯粹, 可以看作特殊类型的函数,其显式地将对象实例或指针作为函数的第一个参数,并且参数可以自己指定,而不强制要求一定是 this或se ...

  8. Springboot:属性常量赋值以及yml配置文件语法(四)

    方式一: 注解赋值 构建javaBean:com\springboot\vo\Dog 1:@Component:注册bean到spring容器中 2:添加get set toString方法 3:使用 ...

  9. C# 反射(一)

    刚工作没多久,需要对以往的知识进行复习和巩固.先讲讲最近在公司常用到的反射机制. 反射(Reflection):提供了封装程序集.模块和类型的对象(Type 类型).可以使用反射动态创建类型的实例,将 ...

  10. JavaScript之预编译

    javascript是一种解释性弱类型语言,在浏览器中执行时,浏览器会先预览某段代码进行语法分析,检查语法的正确与否,然后再进行预编译,到最后才会从上往下一句一句开始执行这段代码,简单得来说可以表示为 ...