数组相关问题

1.数组求和

(1)问题描述

给定一个含有n个元素的整型数组a,求a中所有元素之和,要求只用一行代码

(2)代码

	private static int getSum(int[] a,int endIndex)
	{
		return endIndex==0?a[0]:a[endIndex]+getSum(a,endIndex-1);
	}
	public static void main(String[] args) 
	{
		Random random = new Random();
		int[] a = new int[10];
		for(int i=0; i<10; i++)
		{
			a[i] = random.nextInt(10);
			System.out.print(a[i]+" ");
		}
		System.out.println();
		System.out.println(getSum(a, a.length-1));
	}
结果为:

6 9 5 1 0 5 4 4 0 0 
34


2.数组最大值

(1)问题描述

求出整型数组中的最大值,要求时间复杂度小于O(n)

(2)分析

一般情况下直接遍历一遍就找出最大值,但这里必须要使用分治的方法,将数组分为两部分,分别求出两部分的最大值再进行比较;对左右两部分再进行分割,直至每个部分中只有一个数值

(3)代码

	private static int max(int[] a,int begin, int end)
	{
		if(begin>=end)
			return a[begin];
		else
		{
			int mid = (begin+end)>>1;
			return Math.max(max(a,begin,mid-1), max(a,mid+1,end));
		}
	}
	public static void main(String[] args) 
	{
		Random random = new Random();
		int[] a = new int[10];
		for(int i=0; i<10; i++)
		{
			a[i] = random.nextInt(10);
			System.out.print(a[i]+" ");
		}
		System.out.println();
		System.out.println(max(a,0,a.length-1));
	}
结果为:

4 7 4 2 1 8 9 0 7 2 
9

3.数组中出现次数超过一半的数字

(1)思路1

使用HashMap记录所有数字的出现次数,找出其中出现次数最多的数字

代码:

		Scanner cin = new Scanner(System.in);
		int m = cin.nextInt();
		int n = cin.nextInt();
	
		Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		
		for(int i=0; i<m; i++)
		{
			for(int j=0; j<n; j++)
			{
				int key = cin.nextInt();
				if(map.get(key)==null)
					map.put(key, 1);
				else
				{
					int count = map.get(key);
					if(count>=half)
					{
						System.out.println(key);
						break;
					}
					else
						map.put(key, ++count);
				}
			}
		}

(2)思路2

因为出现次数超过一半,所以对所有数字进行排序,中间的元素必定就为所求

代码:

public static void main(String[] args) 
  {
    Scanner cin = new Scanner(System.in);
    int m = cin.nextInt();
    int n = cin.nextInt();
    int[] a = new int[m*n];
    int half = (m*n)>>1;

    int index = 0;
    for(int i=0; i<m; i++)
    {
      for(int j=0; j<n; j++)
      {
        a[index] = cin.nextInt();
        index++;
      }
    }
    Arrays.sort(a);
    System.out.println(a[half]);
  }


4.数组中距离最近的两个数字(即差的绝对值最小的两个数字)

(1)思路1

对数组中所有数字进行排序,再遍历一遍,记录相邻数字绝对值的最小值

(2)代码

	public static void main(String[] args) 
	{
		Random random = new Random();
		int[] a = new int[10];
		for(int i=0; i<10; i++)
		{
			a[i] = random.nextInt(100);
			System.out.print(a[i]+" ");
		}
		System.out.println();
		int minAbs = Integer.MAX_VALUE;
		Arrays.sort(a);
		for(int i=1; i<a.length; i++)
		{
			if(Math.abs(a[i]-a[i-1])<minAbs)
				minAbs = Math.abs(a[i]-a[i-1]);
		}
		System.out.println(minAbs);
	}
结果为:

87 25 6 24 87 98 30 92 77 98 
0

(3)思路2

使用动态规划,dp[i]表示数组a中从0到i个元素中差绝对值的最小值,min表示a[i]与a[0]到a[i-1]差的绝对值的最小值,那么状态转移方程就为:dp[i] = dp[i-1] , dp[i-1]<min ;dp[i] = min , dp[i-1]<min

(4)代码2

		int[] a = {1,-6,5,3,13,7,-8};
		int[] dp = new int[a.length];
		for(int i=0; i<dp.length; i++)
			dp[i] = Integer.MAX_VALUE;
		
		for(int i=1; i<a.length; i++)
		{
			int min = Integer.MAX_VALUE;
			for(int j=0; j<i; j++)
			{
				if(Math.abs(a[i]-a[j]) < min)
					min = Math.abs(a[i]-a[j]);
			}
			
			dp[i] = dp[i-1]<min?dp[i-1]:min;
		}
		
		System.out.println(dp[a.length-1]);




5.数组中小于0的数字替换为-100,-100,-100三个数字

(1)思路

          因为替换过后数组长度会变长,所以先遍历一遍找出其中小于0的数字的个数,这样就求出了新数组的长度。然后使用两个指针,分别指向原数组的结尾和新数组的结尾

(2)代码

	        int count = 0;
		for(int i=0; i<a.length; i++)
		{
			if(a[i]<0)
				count++;
		}
		
		int[] b = new int[a.length+count*2];
		
		int pb = b.length-1;
		
		for(int i=a.length-1; i>=0; i--)
		{
			if(a[i]>=0)
			{
				b[pb] = a[i];
				pb--;
			}
			else
			{
				b[pb--] = -100;
				b[pb--] = -100;
				b[pb--] = -100;
			}
		}



6.求递增数组中的2个数字,使得这2个数字的和为指定的值,时间复杂度为O(n)

(1)思路

          同时使用两个指针,指向数组的开头和结尾,根据指向数据的和的情况来改变指针移动的方向

(2)代码

		int[] a = {1,2,4,7,11,15};
		int begin = 0;
		int end = a.length-1;
		
		while(begin<end)
		{
			if(a[begin]+a[end]>15)
				end--;
			else if(a[begin]+a[end]<15)
				begin++;
			else
				break;
		}
		if(begin<end)
			System.out.print(a[begin]+" "+a[end]);

               总结:一般可以通过增加指针来降低时间复杂度

(3)扩展

          1)问题描述

                给定一正整数n,求所有和为n的连续正数序列(至少两个数)

          2)思路

                如上,使用两个指针,因为这次需要找出多种方案,所以就不再指向最大和最小的了,指向第一个和第二个,分别为begin、end,记tempSum为a[begin]+.....+a[end]

                1>如果tempSum<n,那么end后移一位,即增大最大的元素

                2>如果tempSum>n,那么begin后移一位,即去掉最小的元素

                3>如果tempSum=n,那么end后移一位(因为如果begin后移那么只是子序列,里面数字完全没有变)

          3)代码

		int[] a = {1,2,3,4,5,6};
		int s = 9;
		
		int begin = 0;
		int end = 1;
		
		while(end<a.length && begin<end)
		{
			int sum = getSum(a, begin, end);
			if(sum < s)
				end++;
			else if(sum > s)
				begin++;
			else
			{
				for(int i=begin; i<=end; i++)
				{
					if(i==begin)
						System.out.print(a[i]);
					else
						System.out.print("+"+a[i]);
				}
				System.out.println();
				end++;
			}
		}

	private static int getSum(int[] a, int begin, int end)
	{
		int sum = 0;
		for(; begin<=end; begin++)
			sum += a[begin];
		
		return sum;
	}

结果为:

2+3+4
4+5


7.查找递增数组中指定数字出现的次数

(1)思路

           因为数组是递增的,可以使用类似二分查找+分治方法

           1>如果中间元素=find,那么次数=左边区间出现次数+右边区间出现次数+1;          

           2>如果中间元素<find,说明查找的元素在右边区间中,那么次数=右边区间出现次数

           3>如果中间元素>find,说明查找的元素在左边区间中,那么次数=左边区间出现次数

(2)代码

	private static int count(int[] a, int find,int begin, int end)
	{
		if(begin<=end)
		{
			int mid = (begin+end)/2;
			if(a[mid] == find)
				return count(a, find, begin, mid-1)+count(a,find,mid+1,end)+1;
			else if(a[mid]<find)
				return count(a,find,mid+1,end);
			else
				return count(a,find,begin,mid-1);
		}
		else
			return 0;
	}

8.数组中数字向右循环移动k位

(1)思路

          引入:输入一个字符串,将该字符串中的所有单词逆序,但是单词内部不逆序。如“hello my java world”输出“world java my hello”

          可以使用先将整个字符串逆序“dlrow avaj ym olleh”,再将各个单词逆序“world java my hello”

         在这里 1,2,3,4右移一位可得4,1,2,3,就相当于先将整个数组逆序4,3,2,1,再将两个子数组逆序

(2)代码

<span style="white-space:pre">	</span>private static void reverse(int[] a, int begin, int end)
	{
		while(begin<end)
		{
			int temp = a[begin];
			a[begin] = a[end];
			a[end] = temp;
			
			begin++;
			end--;
		}
	}

<span style="white-space:pre">		</span>int[] a = {1,2,3,3,3,3,4,5};
		
		int k = 2;
		reverse(a, 0, a.length-1);
		reverse(a, 0, k-1);
		reverse(a, k, a.length-1);
		for(int x:a)
			System.out.print(x+" ");

                     结果为:

4 5 1 2 3 3 3 3 


9.数组中只出现1次的数字(其他数字均出现2次)

(1)思路1

          a^a=0  a^0!=a,所以对整个数组异或操作,最后的结果就是出现1次的数组。

(2)代码

                int temp = 0;
		for(int i=0; i<a.length; i++)
			temp = temp ^ a[i];
		System.out.println(temp);

扩展1:数组中有2个数字出现1次,其余均出现2次,找出2个数字

              先异或一次,得出的就是这两个数字异或的结果,如果结果中某一位为1,则说明这两个数字这一位不同,一个为1,一个为0,那么就根据这一位的不同,将所有数组中数据分为两组,分别找出这两组中唯一出现一次的数字即可。


扩展2:寻找字符串中第一个只出现一次的字符。eg“abeaksks”,则输出“b”

             直接使用哈希表

		String a = "abaccdeff";
		
		Map<Character,Integer> map = new HashMap<Character, Integer>();
		
		for(int i=0; i<a.length(); i++)
		{
			char key = a.charAt(i);
			if(map.containsKey(key))
			{
				int count = map.get(key);
				map.put(key, ++count);
			}
			else
				map.put(key, 1);
		}
		
		//因为HashMap不是按存入的顺序存放的,所以必须查找出现一次且位置最靠前的那个
		int index = a.length();
		for(Entry<Character,Integer> entry:map.entrySet())
		{
			if(entry.getValue()==1)
			{
				if(index > a.indexOf(entry.getKey()))
					index = a.indexOf(entry.getKey());
			}
		}
		
		if(index == -1)//如果不存在只出现一次的
			System.out.println("none");
		else
			System.out.println(a.charAt(index));



10.数组中有0和非0元素,重排数组使得所有0在前面,非0在后面且非0顺序不变

(1)思路1

          从后向前,依次把非0移动到最后,循环完成之后其余直接置0

(2)代码

<span style="white-space:pre">		</span>int[] a = {1,1,0,2,0,3,3,0,0,4,4,5,5,0};
		
		//查找倒数第一个0的元素的下标
		int isZero = a.length-1;
		for(int i= a.length-1; i>=0; i--)
		{
			if(a[i] == 0)
			{
				isZero = i;
				break;
			}
		}
		//每一个非0元素向0元素赋值,同时指向0元素的指针向前
		for(int i= isZero-1; i>=0; i--)
		{
			if(a[i] != 0)
			{
				a[isZero] = a[i];
				isZero--;
			}
		}
		
		for(int i=0; i<=isZero; i++)
			a[i] = 0;


11.调整数组中元素,使得奇数在偶数前面

(1)思路

          类似于快排的调整,设置两个指针,分别指向开始和结尾,前者指向的为奇数,后者指向的为偶数,交换两者,直至两个指针相遇

(2)代码

		int[] a = { 1, 2, 3,4,5};
		
		int odd = 0;
		int even = a.length-1;
		
		while(odd < even)
		{
			while(a[even]%2==0 && even>odd)
				even--;
			
			while(a[odd]%2!=0 && odd<even)
				odd++;
			
			if(odd < even)
			{
				int temp = a[odd];
				a[odd] = a[even];
				a[even] = temp;
				
				odd++;
				even--;
			}
		}
		
		for(int x:a)
			System.out.print(x+" ");


12.合并两个有序数组

(1)思路

          先不管谁先结束,直接合并,然后再判断谁结束了

(2)代码

		int[] a1 = {1,3,5,7};
		int[] a2 = {2,4,6};
		
		int[] a = new int[a1.length+a2.length];
		
		int i = 0;
		int j = 0;
		int k = 0;
		
		while(i<a1.length && j<a2.length)
		{
			if(a1[i]<a2[j])
			{
				a[k] = a1[i];
				i++;
			}
			else
			{
				a[k] = a2[j];
				j++;
			}
			
			k++;
		}
		
		if(i==a1.length)
		{
			for(;j<a2.length; j++,k++)
				a[k] = a2[j];
		}
		else if(j==a2.length)
		{
			for(;i<a1.length; i++,k++)
				a[k] = a1[i];
		}
		
		for(int x:a)
			System.out.print(x+" ");
	}
结果为:

1 2 3 4 5 6 7 








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值