上一篇文章我们已经了解过了链表的相关基础知识,现在总结几个写链表代码的技巧,如能熟练掌握这些那么就可以轻松的写出链表的代码。。。。
技巧一、理解指针或引用的含义
事实上链表的结构并不是很难,但是一旦将它和“引用”混在一起的话就会很难了,想要写对链表代码首先就要理解好指针。。
下面会用Java语言举例子。。一段代码实例化的时候实例就可以理解为指针,指向创建的对象地址。。
技巧二、警惕指针丢失和内存泄漏
对于C语言来说内存管理是由程序员负责的,如果没有手动释放结点对应的内存空间,就会产生内存泄漏。但是对于像Java这种虚拟机自动管理内存的编程语言来说,就不需要考虑这么多了。。。
利用哨兵简化实现难度
哨兵就是解决国家之间的边界问题。同理这里说的哨兵也是解决“边界问题”,不直接参与业务逻辑。
如果引入哨兵结点,在任何时候不管链表是不是空,head指针都会一直指向这个哨兵结点。我们也把这种哨兵结点的链表叫带头链表。相反没有哨兵结点的链表就叫做不带头链表。
有一点是哨兵结点是不存储数据的,哨兵结点一直都在。。。
实际上利用哨兵简化编程难度的技巧在很多程序中都会出现,比如插入排序、归并排序、动态规划等等。。。
//在数组a中,查找key,返回key所在的位置
//其中,n表示数组a 的长度
public int getKey(char []a ,int n, char key){
//边界条件处理,如果a为空,或者n<=0,说明数组中没有数据,就不用while循环比较了
if (a == null || n <= 0) {
return -1;
}
int i = 0;
//这里有两个比较操作,i<n和a[i]==key
while (i < n) {
if (a[i] == key) {
return i;
}
++i;
}
return -1;
}
代码二:
public static void main(String[] args) {
int[] a = {4, 2, 3, 5, 9, 6};
int n = 6;
int key = 7;
new LinkedDemo1().find(a,n,key);
}
//在数组a中,查找key,返回key所在的位置
//其中,n表示数组a 的长度
public int find(int []a ,int n, int key){
if (a == null || n <= 0) {
return -1;
}
//这里因为要将a[n-1] 的值替换为key,所以要特殊处理这个值
if (a[n - 1] == key) {
return n - 1;
}
//把a[n-1]的值临时保存在变量tmp中,以便之后恢复。tmp=6
//原因是:希望find不要改变a内容
int tmp = a[n - 1];
//把key的值放进a[n-1]中,此时a = {4,2,3,5,9,7}
a[n - 1] = key;
int i = 0;
//while循环比起代码1,少了i<n这个比较操作
while(a[i]!=key){
++i;
}
//恢复a[n-1]原来的值,此时a={4,2,3,5,9,6}
a[n - 1] = tmp;
if (i == n - 1) {
//如果i == n-1说明,在0...n-2之间都没有key,所以返回-1
return -1;
} else {
//否则,返回i,就是等于key值的元素的下标
return i;
}
}
当字符串很长的时候代码二会运行的很快,执行次数最多的是while那一部分。第二段代码中通过一个哨兵a[n-1] = key,成功省掉了一个比较语句i < n。
技巧四、重点留意边界条件处理
检查链表代码是否正确的边界条件有这么几个:
- 如果链表为空,代码是否能正常工作
- 如果链表只包含一个结点时,代码是否正常工作
- 如果链表只包含两个结点时,代码是否正常工作
- 代码逻辑在处理头结点和尾结点的时候,是否能正常工作
技巧五、举例画图,辅助思考
对于稍微复杂的链表操作,例如单链表反转,转向很晕感觉自己的脑容量不是很多因此这个时候就要放大招了:举例法和画图法。