* 题目:在数组中的两个数字,如果前面的数字大于后面的数字,则这两个数字组成一个逆序对。
* 输入一个数组,求出数组中的逆序对总数
* 例如:在数组{7,5,6,4}中,一共存在5个逆序对,分别是{7,5}{7,6}{7,4}{5,4}{6,4}
*
* 思路:统计逆序对
* 1> 将数组分割成子数组,再将子数组进行分割 ,直到分割为长度为1的数组
* 2> 统计子数组中最短的数组(两两进行合并)之间的逆序对的数目,然后再统计相邻的子数组中的逆序对的数目,直到合并为一个整体之后
* 其中,在2>中进行统计逆序对的数目时,需要构建一个辅助数组,将两个数组合并后的新数组存入,构建两个索引,都从最后一个开始
* 比较两个指针指向的值,
* (1)若第一个数组中的值大于第二个数组中的值,则逆序对的数目等于第二个数组中前面还剩余元素个数,
* 并将较大的数存入辅助数组,然后将较大数所在的数组的指针和辅助数组的指针向前移动
* (2)若第一个数组的值小于等于第二个数组中的值,则无逆序对
* 并将较大的数存入辅助数组,然后将较大数所在的数组的指针和辅助数组的指针向前移动
*
* 时间复杂度O(nlogn) 空间复杂度O(n) 以空间换时间的算法
*
* 其实现过程实际上就是2-路归并排序的过程:
* 归并:将两个或两个以上的有序表组合成一个新的有序表
*
* 初始序列含有n个记录,则可以看成时有n个子序列,每个子序列的长度为1,然后进行两两合并,得到n/2(取整)个长度为2或者1的有序子序列。
* 然后再进行两两合并,。。。,直到得到一个长度为n的有序序列为止
package Test;
public class No51InversePairs {
/*
* 面试题51:数组中的逆序对
* 题目:在数组中的两个数字,如果前面的数字大于后面的数字,则这两个数字组成一个逆序对。
* 输入一个数组,求出数组中的逆序对总数
* 例如:在数组{7,5,6,4}中,一共存在5个逆序对,分别是{7,5}{7,6}{7,4}{5,4}{6,4}
*
* 思路:统计逆序对
* 1> 将数组分割成子数组,再将子数组进行分割 ,直到分割为长度为1的数组
* 2> 统计子数组中最短的数组(两两进行合并)之间的逆序对的数目,然后再统计相邻的子数组中的逆序对的数目,直到合并为一个整体之后
* 其中,在2>中进行统计逆序对的数目时,需要构建一个辅助数组,将两个数组合并后的新数组存入,构建两个索引,都从最后一个开始
* 比较两个指针指向的值,
* (1)若第一个数组中的值大于第二个数组中的值,则逆序对的数目等于第二个数组中前面还剩余元素个数,
* 并将较大的数存入辅助数组,然后将较大数所在的数组的指针和辅助数组的指针向前移动
* (2)若第一个数组的值小于等于第二个数组中的值,则无逆序对
* 并将较大的数存入辅助数组,然后将较大数所在的数组的指针和辅助数组的指针向前移动
*
* 时间复杂度O(nlogn) 空间复杂度O(n) 以空间换时间的算法
*
* 其实现过程实际上就是2-路归并排序的过程:
* 归并:将两个或两个以上的有序表组合成一个新的有序表
*
* 初始序列含有n个记录,则可以看成时有n个子序列,每个子序列的长度为1,然后进行两两合并,得到n/2(取整)个长度为2或者1的有序子序列。
* 然后再进行两两合并,。。。,直到得到一个长度为n的有序序列为止
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No51InversePairs p = new No51InversePairs();
int[] array = {7,5,6,4};//其中array{0] = 7
System.out.println("数组中逆序对一共有:"+p.InversePairs(array));
}
int count = 0;
//统计逆序对的数目
private int InversePairs(int[] array) {
// TODO Auto-generated method stub
if(array == null || array.length == 0)
return 0;
mergeSort(array,0,array.length-1);
return count;
}
//将长度为n的数组分割为多个子数组
private void mergeSort(int[] array, int start, int end) {
// TODO Auto-generated method stub
int mid = (start + end)/2;
if(start < end) {
//递归到 分割的小小的单位
mergeSort(array,start,mid);
mergeSort(array,mid+1,end);
//然后将两个子数组进行合并
merge(array,start,mid,end);
}
}
//将两个子数组合并
private void merge(int[] array, int start, int mid, int end) {
// TODO Auto-generated method stub
//辅助数组 存储合并后的数组 长度为end - start + 1
int[] copy = new int[end - start +1];
//然后声明三个指针分别指向第一个子数组的末位,第二个子数组的末位,辅助数组的末位
int p1 = mid ;
int p2 = end;
int p3 = end - start;
//p3= start && p2 >= mid+1) {
//若第一个数组中元素大于第二个数组
//则逆序对的数量为 第二个数组中还剩余的元素个数
if(array[p1] > array[p2]) {
count += (p2 - mid);
copy[p3--] = array[p1--];
}
//若小于,则无逆序对
else {
copy[p3--] = array[p2--];
}
}
while(p1 >= start)
copy[p3--] = array[p1--];
while(p2 >= mid+1)
copy[p3--] = array[p2--];
for(int i = 0; i < copy.length;i++ )
array[start++] = copy[i];
}
}
* 题目:输入两个链表,找出他们的第一个公共系欸但
* 链表节点定义:
* struct ListNode{
* int m_nKey;
* ListNode* m_pNext;
* }
*
* 思路:链表1 长度为m 链表2长度为n
* 第一种方法:蛮力法 时间复杂度O(mn)
* 从头遍历第一个链表,和第二个链表进行对比 一样的话就是公共节点
*
* 第二种方法:使用辅助栈 时间复杂度O(m+n) 空间复杂度O(m+n) 空间换时间的算法
* 对于有公共节点的单向链表来说,会成为一个Y字型的结构,从尾部进行遍历的话,
* 直接找到第一个公共子节点就可以了,而且长度会大大缩小
* 因为单向链表是从头进行遍历的,而从尾部进行比较会减少比较次数,这种情况很像是后进先出的情形,也就是栈的存储结构
* 所以可以使用两个栈,对两个单向链表进行存储,然后存储后,弹出元素进行对比即可,找到最后一个相等的子节点
*
* 第三种方法:无辅助空间 时间复杂度O(m+n) 空间复杂度O(1) 提高了空间效率
* 当两个链表的长度不相同时,从头节点遍历,会造成到达尾节点的时间不同,为解决这个问题,可以:
* 1>第一次遍历两个链表得到它们的长度,算出相差多少LengthDiffer
* 2>第二次遍历时,先将长的链表先走LengthDiffer步,然后两个链表再同步遍历,
* 直到找到第一个相同的节点就是他们的第一个公共节点
*
* 本例使用第三个方法实现
package Test;
public class No52FindFirstCommonNode {
/*
* 面试题52:两个链表中的第一个公共节点
* 题目:输入两个链表,找出他们的第一个公共系欸但
* 链表节点定义:
* struct ListNode{
* int m_nKey;
* ListNode* m_pNext;
* }
*
* 思路:链表1 长度为m 链表2长度为n
* 第一种方法:蛮力法 时间复杂度O(mn)
* 从头遍历第一个链表,和第二个链表进行对比 一样的话就是公共节点
*
* 第二种方法:使用辅助栈 时间复杂度O(m+n) 空间复杂度O(m+n) 空间换时间的算法
* 对于有公共节点的单向链表来说,会成为一个Y字型的结构,从尾部进行遍历的话,
* 直接找到第一个公共子节点就可以了,而且长度会大大缩小
* 因为单向链表是从头进行遍历的,而从尾部进行比较会减少比较次数,这种情况很像是后进先出的情形,也就是栈的存储结构
* 所以可以使用两个栈,对两个单向链表进行存储,然后存储后,弹出元素进行对比即可,找到最后一个相等的子节点
*
* 第三种方法:无辅助空间 时间复杂度O(m+n) 空间复杂度O(1) 提高了空间效率
* 当两个链表的长度不相同时,从头节点遍历,会造成到达尾节点的时间不同,为解决这个问题,可以:
* 1>第一次遍历两个链表得到它们的长度,算出相差多少LengthDiffer
* 2>第二次遍历时,先将长的链表先走LengthDiffer步,然后两个链表再同步遍历,
* 直到找到第一个相同的节点就是他们的第一个公共节点
*
*
* */
static class ListNode{
int val;
ListNode next;
ListNode(int val){
this.val = val;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
No52FindFirstCommonNode f = new No52FindFirstCommonNode();
ListNode head1 = new ListNode(1);
ListNode head2 = new ListNode(2);
ListNode three = new ListNode(3);
ListNode four = new ListNode(4);
ListNode five = new ListNode(5);
ListNode six = new ListNode(6);
ListNode seven = new ListNode(7);
ListNode eight = new ListNode(8);
//链表1
head1.next = three;
three.next = five;
//链表2
head2.next = four;
four.next = five;
//公共
five.next = seven;
seven.next = null;
ListNode node1 = head1;
System.out.println("输出链表1数据:");
for(int i=0;i<4;i++) {
System.out.println(node1.val);
node1 = node1.next;
}
ListNode node2 = head2;
System.out.println("输出链表2数据:");
for(int i=0;i<4;i++) {
System.out.println(node2.val);
node2 = node2.next;
}
System.out.println("两个链表的第一个公共节点:");
ListNode firstCommonNode = f.FindFirstCommonNode(head1,head2);
System.out.println(firstCommonNode.val);
System.out.println("****************************"+"两个链表的第一个公共节点"+"****************************");
}
//两个链表的第一个公共节点
private ListNode FindFirstCommonNode(ListNode head1, ListNode head2) {
// TODO Auto-generated method stub
ListNode p1 = head1;
ListNode p2 = head2;
int size1 = 0;
int size2 = 0;
//遍历得到两个链表的长度
while(p1 != null) {
size1++;
p1 = p1.next;
}
while(p2 != null) {
size2++;
p2 = p2.next;
}
//从头再进行遍历
//但是要将长链表先走他们的长度差
p1 = head1;
p2 = head2;
//若第一个链表长 第一个链表先走一定长度
if(size1 > size2) {
for(int i = 0;i < size1-size2;i++) {
p1 = p1.next;
}
}
//若第二个链表长
else if(size1 < size2){
for(int i = 0;i < size2-size1;i++) {
p2 = p2.next;
}
}
//两个链表同时进行比较 直到找到第一个公共节点
while(p1 != p2 && p1 != null && p2 != null) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
}