1. 配列内に半分以上出現する数値
配列内に配列の長さの半分以上に出現する数値があります。この数値を見つけてください。
配列は空ではなく、指定された配列には常に大部分の要素が存在すると想定できます。
例 1:
入力: [1, 2, 3, 2, 2, 2, 5, 4, 2]
出力: 2
はオファー 39 を参照します。配列内に半分以上出現する数値
1.1 並べ替え
タイトルに数字の出現数が配列の長さの半分を超えると規定されているので、ソートするだけで済み、中央の要素が応答結果になります。
バブリングやクイックソートなど、さまざまなソート方法があります。
バブルソートを使用しようとしましたが、時間切れです。
素早い列。
public int majorityElement(int[] nums) {
quickSort(nums,0,nums.length-1);
return nums[nums.length/2];
}
public void quickSort(int [] nums,int low,int high){
if(high <= low) return;
int partition = partition(nums,low,high);
quickSort(nums,low,partition-1);
quickSort(nums,partition+1,high);
}
public int partition(int [] nums,int low,int high){
int part = nums[low];
int left = low;
int right = high;
while(left<right){
while(left<right && nums[right] > part){
right--;
}
nums[left] = nums[right];
while(left<right && nums[left] <= part){
left++;
}
nums[right] = nums[left];
}
nums[left]= part;
return left;
}
せめて速度は上がるだろうとクイックソートを使ってみましたが、明らかにパフォーマンスは高くありません。
Arrays.sort() を使用する方
が少し良いようです。
1.2 マップ
要素の出現数を統一するには、ハッシュマップを使用し、要素を k に設定し、出現数を value に設定し、出現数が配列の長さの半分より大きい場合に要素を見つけることができます。また、通過する必要があるのは 1 回だけです。
public int majorityElement(int[] nums) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
if(map.get(nums[i]) > nums.length/2){
return nums[i];
}
}
return 0;
}
時間計算量: O(n)、不可避的に配列を 1 回走査する必要がある
空間計算量: O(n)、ハッシュ テーブルを使用して格納する
ただし、時間的な効果はまだ明らかではなく、ループ内でハッシュ テーブル内の要素が要件を満たしているかどうかを判断する必要があります。
1.3 投票アルゴリズム
主なアイデアは 2 つの変数を使用し、1 つの結果には配列の番号が格納され、もう 1 つの結果には出現回数が格納され、現在の数値が次の数値と同じかどうかを判断します。数値は 1 つ増加します。そうでない場合は、最後の数値が 0 (間違いなく結果ではないことを意味します) になるまで、数値は 1 ずつ減ります。その後、結果は次の要素 (要素である必要があります) を指します。数値が加算または減算され、それが = 1 (対象条件) 以上の場合、この数値が返されます。
上記は少しわかりにくいです。たとえば [1, 2, 3, 2, 2, 2, 5, 4, 2]
public int majorityElement(int[] nums) {
int count =0;
Integer result = null;
for(int num:nums){
if(count == 0){
result =num;
}
count+=(num==result)?1:-1;
}
return result;
}
時間計算量: O(n)
空間計算量: O(1)
2. 数字は 1 回だけ表示されます
1 回だけ現れる数値 空では
ない整数配列 nums が与えられると、1 回だけ現れる特定の要素を除いて、各要素は 2 回現れます。1 回だけ出現する要素を見つけます。
この問題を解決するには、一定の追加スペースのみを使用する線形時間計算量のアルゴリズムを設計して実装する必要があります。
例 1:
入力: nums = [2,2,1]
出力: 1
2.1 ハッシュセット
ハッシュセットの主な機能は重複を排除することです。この数値が以前に出現したかどうかを判断するだけです。出現した場合はこの要素を削除すると、最後に望ましい結果が残ります。
public int singleNumber(int[] nums) {
HashSet<Integer> hashSet = new HashSet<>();
for(int i=0;i<nums.length;i++){
if(!hashSet.contains(nums[i])){
hashSet.add(nums[i]);
}else{
hashSet.remove(nums[i]);
}
}
return hashSet.toArray(new Integer[hashSet.size()])[0];
}
時間計算量: O(n) のリンク リストの走査、配列に変換 時間計算量 O(m)、
空間計算量: O(m) 一意の要素の数、ただしここでは 1 つだけが表示されるため、計算量は O( 1 );
2.2 XOR
XOR の特徴:
0^ a= a;
a^a=0;
a^b ^ a =b
タイトルの条件は、他の要素が 2 つだけ出現し、2 回後のオフセットが 0 になることです。
public int singleNumber(int[] nums) {
int flag = 0;
for(int num: nums){
flag^=num;
}
return flag;
}
時間計算量: O(n)
空間計算量: O(1)
3 剣はオファー 03 を指します。配列内の繰り返しの数字
剣はオファー 03 を指します。 配列内の繰り返しの数値
長さ n の配列 nums 内のすべての数値は、0 ~ n-1 の範囲にあります。配列内の一部の数値が繰り返されていますが、繰り返される数値の数がわかりません。また、各数値が何回繰り返されるかもわかりません。配列内で繰り返される数値を見つけてください。
入力:
[2、3、1、0、2、5、3]
出力: 2 または 3
3.1 ハッシュマップの使用
走査では要素をハッシュ テーブルに格納しますが、回数が 1 より大きい場合は要素が重複していることを意味し、要素を直接返すことができます。具体的なアプローチは最初の質問と同様です。ハッシュセットも使用できます。
public int findRepeatNumber(int[] nums) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
if(map.get(nums[i]) >1){
return nums[i];
}
}
return 0;
}
時間計算量: O(n)
空間計算量: O(n)
3.2 インプレース交換
タイトル条件は、配列要素の値が 0-n-1 の間であることを示しています。対応する添え字に従って要素を並べ替えるだけで済みます。最初の添え字に繰り返し値がある場合、それは繰り返し要素です。
public int findRepeatNumber(int[] nums) {
int i =0;
while(i<nums.length){
if(nums[i] == i){
i++;
continue;
}
if(nums[nums[i]] == nums[i]) return nums[i];
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
return -1;
}
時間計算量: O(n)
空間計算量: O(1)
3. 色の分類(オランダ国旗)
カラーソート
赤、白、青の n 個の要素を含む配列 nums を指定すると、同じ色の要素が隣接して赤、白、青の順に配置されるように、それらをインプレースでソートします。
整数 0、1、2 を使用して、それぞれ赤、白、青を表します。
この問題は、ライブラリの組み込みソート機能を使用せずに解決する必要があります。
3.1 バブルソート + ダブルポインタ
主なアイデアはまだ浮かんでいませんが、最初に 0 要素を左端に配置し、その後 0 要素がすべて配置されます。このとき、左の添え字が 1 の開始位置となり、その後 1 が 2 に再度交換されます。 。
public void sortColors(int[] nums) {
int left =0;
// 先将0都交换到最左边
for (int right = 0; right < nums.length; right++) {
if(nums[right] == 0 ){
int temp = nums[right];
nums[right] = nums[left];
nums[left] = temp;
left++;
}
}
// 将1都交换到2的左边,这里必须要先++,因为此时的left还在0元素
for(int right = left ;right<nums.length;++right){
if(nums[right] == 1){
int temp = nums[right];
nums[right] = nums[left];
nums[left] = temp;
++left;
}
}
}
時間計算量: O(n*2)
空間計算量: O(1)
4.2 マルチポインタのワンタイムトラバーサル
まず、先頭を指すポインタと末尾を指すポインタの 2 つが必要ですが、位置の移動や交換にはポインタが必要です。左は 0 の位置をロックすることに相当し、右は 2 の位置をロックすることに相当します。
public void sortColors(int[] nums) {
int left = 0;
int right = nums.length -1;
int index =0;
while(index <= right){
if(nums[index] == 0){
swap(nums,index++,left++);
}else if(nums[index] == 2){
swap(nums,index,right--);
}else{
index++;
}
}
}
public void swap(int [] nums,int i,int j){
int temp = nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
時間計算量: O(n)
空間計算量: O(1)
要約する
配列などの問題の場合は、通常、並べ替え、移動、およびそのような問題の繰り返しです。