1、为了找出倒数第k个元素,最容易想到的办法是首先遍历一遍单链表,求出整个单链表的长度n,然后将倒数第k个,转换为正数第n-k个,接下来遍历一次就可以得到结果。但是该方法存在一个问题,即需要对链表进行两次遍历,第一次遍历用于求解单链表的长度,第二次遍历用于查找正数第n-k个元素。
2、显然,这种方法还可以进行优化。于是想到了第二种方法,如果从头至尾的方向从链表中的某个元素开始,遍历k个元素后刚好达到链表尾,那么该元素就是要找到的倒数第k个元素,根据这一性质,可以设计如下算法:从头节点开始,依次对链表的每一个节点元素进行这样的测试,遍历k个元素,查看是否到达链表尾,只到找到哪个倒数第k个元素。此种方法将对同一批元素进行反复多次的遍历,对于链表中的大部分元素而言,都要遍历K个元素,如果链表长度为n个的话,该算法的时间复杂度为O(kn)级,效率太低。
3、存在另外一个更高效的方式,只需要一次遍历即可查找到倒数第k个元素。由于单链表只能从头到尾依次访问链表的各个节点,因此,如果要找到链表的倒数第k个元素的话,也只能从头到尾进行遍历查找,在查找过程中,设置两个指针,让其中一个指针比另一个指针先前移k-1步,然后两个指针同时往前移动。循环直到线性的指针值为NULL时,另一个指针所指向的位置就是所要找到的位置。代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace repicropal
{
class Program
{
static void Main(string[] args)
{
LinkedList<string> list = new LinkedList<string>();
list.AddLast("1");
list.AddLast("2");
list.AddLast("3");
list.AddLast("4");
Console.WriteLine(GetReciprocalElement2(list, 4).Value);
}
static LinkedListNode<string> GetReciprocalElement1(LinkedList<string> list, int reciprocalIndex)
{
LinkedListNode<string> temp = null;
LinkedListNode<string> head = list.First;
int index = 1;
if (list.Count < reciprocalIndex) return null;
while (head != null)
{
//index达到倒数的位数时
if (reciprocalIndex == index)
{
//temp从head开始,也就是temp比head先跑index个步数
temp = head;
//head从头开始
head = list.First;
}
if (temp != null)
{
//temp == null 说明temp已经到达链表尾部
if (temp.Next == null)
{
//此时head刚好到达倒数的指定位置
temp = head;
break;
}
}
//累加步数
index++;
//temp向前移动
if (temp != null)
temp = temp.Next;
//head 向前移动
head = head.Next;
}
Console.WriteLine(index+"循环次数 "+temp.Value);
return temp;
}
static LinkedListNode<string> GetReciprocalElement2(LinkedList<string> list, int reciprocalIndex)
{
LinkedListNode<string> temp = list.First;
LinkedListNode<string> head = list.First;
//让head先跑reciprocalIndex步
while (--reciprocalIndex >= 0)
{
head = head.Next;
}
//temp从头开始跑,head继续从当前位置开始跑,一直到head跑到最后
while (head != null)
{
temp = temp.Next;
head = head.Next;
}
return temp;
}
}
}