一、C语言实现

1. 先获取链表长度,再从正向遍历

/** * Definition for singly-linked list. * struct ListNode { *     int val; *     struct ListNode *next; * }; */// 比拟奢侈的算法// 先求出链表长度,而后依据长度求出要删除的元素正向的地位// 最初遍历到该元素前一元素进行删除操作struct ListNode *removeNthFromEnd(struct ListNode *head, int n){  int len = 0;  struct ListNode *origin = head;   // 计算链表的长度  while (head)  {    len++;    head = head -> next;  }  // 将head从新指向头结点  head = origin;  // 计算是正向第几个节点  int count = len - n;  // 如果是正向第一个结点,则头结点被删除,只须要间接返回头结点的next即可  if (count == 0) {    return head -> next;  }   // 如果不是正向第一个结点(即结点),则间接遍历到该结点的前一个结点  for (int i = 0; i < count - 1; i++) {    head = head -> next;  }  // 将该节点删除,而后返回头结点的指针  head -> next = head -> next -> next;  return origin;}

2. 双指针法,只须要遍历一次

/** * Definition for singly-linked list. * struct ListNode { *     int val; *     struct ListNode *next; * }; *///  双指针法,挪动first指针使其到的链表末端,同时挪动second指针//  使其指向最终要删除的节点的前一个节点,此时即可删除节点并返回头结点//  只需一轮循环即可实现struct ListNode *removeNthFromEnd(struct ListNode *head, int n){  // 创立一个哑结点,并将其指向头结点  struct ListNode dumpy = {0, head};  // 将first指针指向head头指针  struct ListNode *first = head;  // 将second指针指向哑结点,此处dumpy是构造体,因而须要取地址  struct ListNode *second = &dumpy;  for (int i = 0; i < n; i++)  {    first = first->next;  }  while (first)  {    first = first->next;    second = second->next;  }  second->next = second->next->next;  // first second和head都是构造体指针,因而能够应用 -> 拜访成员  // dumpy是构造体,能够间接用点号拜访  return dumpy.next;}

二、JS实现

1. 先获取链表长度,再从正向遍历

/** * Definition for singly-linked list. * function ListNode(val, next) { *     this.val = (val===undefined ? 0 : val) *     this.next = (next===undefined ? null : next) * } *//** * @param {ListNode} head * @param {number} n * @return {ListNode} */// 比拟奢侈的算法// 先求出链表长度,而后依据长度求出要删除的元素正向的地位// 最初遍历到该元素前一元素进行删除操作const removeNthFromEnd = function (head, n) {  const origin = head  if (!head) {    return null  }  let count = 1  while (head.next) {    count++    head = head.next  }  head = origin  if (count <= n) {    return origin.next  } else {    // 倒数第n个数,就是负数第count - n + 1个数,那么,只须要遍历到此数的前一个数,即可    // 前一个数则是第count - n个数,从第一个数开始遍历,只需遍历count - n - 1次就可达到此数    const index = count - n - 1    for (let i = 0; i < index; i++) {      head = head.next    }    head.next = head.next.next  }  return origin}

2. 双指针法,只须要遍历一次

/** * Definition for singly-linked list. * function ListNode(val, next) { *     this.val = (val===undefined ? 0 : val) *     this.next = (next===undefined ? null : next) * } *//** * @param {ListNode} head * @param {number} n * @return {ListNode} *///  双指针法,挪动first指针使其到的链表末端,同时挪动second指针//  使其指向最终要删除的节点的前一个节点,此时即可删除节点并返回头结点//  只需一轮循环即可实现const removeNthFromEnd = function (head, n) {  // 创立一个哑结点,并将它指向头结点  const dumpy = new ListNode(0, head)  // 将第二个指针指向哑结点,第一个指针指向头结点  let second = dumpy  let first = head  // 将第一个指针往前挪动n个地位  for (let i = 0; i < n; i++) {    first = first.next  }  // 挪动第一个指针直到链表结尾,同时挪动第二个指针  while (first) {    first = first.next    second = second.next  }  // 此时第二个指针指向的是倒数第n个节点的前一个节点  // 将倒数第n个节点删除即可  second.next = second.next.next  // 返回头结点,应用head会出错,为什么呢?因为如果要删除的节点是头结点,那么头结点被删除后,  // 哑结点间接指向第二个结点,而head还是指向头结点,因而会报错。  // 所以此时要么返回head.next,要么返回dumpy.next。  // 而head.next的返回须要判断second.next是不是指向头结点  // 而dumpy.next则间接应用  return dumpy.next}

三、算法工夫及空间复杂度

1. 链表长度法的工夫及空间复杂度

  • 工夫复杂度:O(L),其中L是链表的长度。
  • 空间复杂度:O(1)。

2. 双指针法

  • 工夫复杂度:O(L),其中L是链表的长度。
  • 空间复杂度:O(1)。